一、项目初始化与环境准备
本文档描述基于 Angular 构建的流媒体服务落地页(Landing Page)的前端代码结构与功能,包括共享模块、路由配置、组件实现等。所有代码均保持原样,仅添加解释性说明。
//
一、项目初始化与环境准备
在开发此项目前,通常需要全局安装 Angular CLI 并创建项目,同时可选添加服务端渲染(SSR)支持。以下是你已执行的命令及其作用:
npm install -g @angular/cli
- 作用:全局安装 Angular 命令行工具,便于创建、开发和构建 Angular 项目。
ng add @angular/ssr
- 作用:为现有 Angular 项目添加服务端渲染(Server-Side Rendering)支持,提升首屏加载速度和 SEO 表现。在选择配置时,你选择了
no和css,即不生成服务端路由文件,并采用纯 CSS 样式(未使用 Sass/Less)。
二、共享模块(SharedModule)
2.1 文件:shared-module.ts
路径:src/app/shared/shared-module.ts
功能:集中管理、导入并导出项目中常用的 Angular Material 模块、表单模块以及路由模块,避免在多个特性模块中重复导入,提高代码复用性和维护性。
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
// Angular Material 组件模块导入
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatMenuModule } from '@angular/material/menu';
import { MatDividerModule } from '@angular/material/divider';
import { MatCardModule } from '@angular/material/card';
import { MatSelectModule } from '@angular/material/select';
import { MatChipsModule } from '@angular/material/chips';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatDialogModule } from '@angular/material/dialog';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSliderModule } from '@angular/material/slider';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatExpansionModule } from '@angular/material/expansion';
import { RouterModule } from '@angular/router';
// 将需要共享的模块集中在一个数组中
const MATERIAL_MODULES = [
MatIconModule,
MatButtonModule,
MatFormFieldModule,
MatInputModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatTooltipModule,
MatMenuModule,
MatDividerModule,
MatCardModule,
MatSelectModule,
MatChipsModule,
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatDialogModule,
MatSnackBarModule,
MatSlideToggleModule,
MatSliderModule,
MatToolbarModule,
MatExpansionModule
];
@NgModule({
declarations: [],
// 导入:CommonModule、RouterModule、表单模块(模板驱动 + 响应式)以及所有 Material 模块
imports: [RouterModule, FormsModule, ReactiveFormsModule, CommonModule, ...MATERIAL_MODULES],
// 导出:使任何导入本模块的模块都能直接使用这些模块
exports: [RouterModule, FormsModule, ReactiveFormsModule, CommonModule, ...MATERIAL_MODULES]
})
export class SharedModule {}
⚠️ 编译错误提示:当前代码缺少对
FormsModule和ReactiveFormsModule的导入语句。你需要在文件顶部添加:import { FormsModule, ReactiveFormsModule } from '@angular/forms';否则 TypeScript 编译器会报错
Cannot find name 'FormsModule'。
三、根模块与路由配置
3.1 文件:app-module.ts
路径:src/app/app-module.ts
功能:Angular 应用的根模块,负责引导启动、声明根组件和可路由组件、导入核心模块以及配置全局提供者。
import { NgModule, provideBrowserGlobalErrorListeners } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing-module';
import { App } from './app';
import { Landing } from './landing/landing';
import { SharedModule } from './shared/shared-module';
import { Signup } from './signup/signup';
import { provideHttpClient } from '@angular/common/http';
@NgModule({
// 声明本模块拥有的组件/指令/管道
declarations: [App, Landing, Signup],
// 导入其他模块
imports: [BrowserModule, AppRoutingModule, SharedModule],
// 全局提供者:错误监听器、HTTP 客户端
providers: [provideBrowserGlobalErrorListeners(), provideHttpClient()],
// 启动时引导的根组件
bootstrap: [App],
})
export class AppModule {}
provideHttpClient():提供 Angular 的 HTTP 客户端服务,用于调用后端 API。provideBrowserGlobalErrorListeners():提供全局错误监听,便于捕获未处理的异常。
3.2 文件:app-routing-module.ts
路径:src/app/app-routing-module.ts
功能:定义应用级路由规则,将 URL 路径映射到对应的组件。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { Landing } from './landing/landing';
import { Signup } from './signup/signup';
const routes: Routes = [
{ path: '', component: Landing }, // 根路径显示落地页
{ path: 'signup', component: Signup }, // 注册页
// { path: 'login', component: Login }, // 登录页(尚未实现)
{ path: '**', redirectTo: '', pathMatch: 'full' }, // 通配符重定向至首页
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
- 通配符路由
**用于处理用户访问不存在的路径,自动跳转回首页。 login路由被注释,表明登录页面尚未开发。
四、落地页组件(Landing)
落地页是用户进入应用的第一屏,包含品牌展示、邮箱收集、特色功能介绍、FAQ 等模块。
4.1 组件类文件:landing.ts
路径:src/app/landing/landing.ts
功能:处理落地页的交互逻辑,包括表单验证、导航跳转、动态数据的定义等。
import { Component } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
@Component({
selector: 'app-landing',
standalone: false,
templateUrl: './landing.html',
styleUrl: './landing.css',
})
export class Landing {
landingForm!: FormGroup; // 邮箱输入表单组
year = new Date().getFullYear(); // 当前年份,用于页脚版权信息
constructor(
private fb: FormBuilder,
private router: Router
) {
// 初始化响应式表单,包含 email 字段及验证规则
this.landingForm = this.fb.group({
email: ['', [Validators.required, Validators.email]]
});
}
// 跳转到登录页(暂未实现路由,实际点击会尝试导航到 '/login')
login() {
this.router.navigate(['/login']);
}
// 获取邮箱并跳转到注册页,将邮箱作为查询参数传递
getStarted() {
const email = this.landingForm.get('email')?.value?.toString().trim() || '';
console.log('getStarted called, email=', email);
if (!email) {
console.warn('getStarted aborted: empty email');
return;
}
this.router.navigate(['/signup'], { queryParams: { email } })
.then(ok => console.log('router.navigate result:', ok))
.catch(err => console.error('router.navigate error:', err));
}
// “为什么选择 Pudplanet” 的理由列表,用于在模板中循环渲染
reasons = [
{ title: 'Enjoy on your TV.', text: 'Watch on Smart TVs, PlayStation, Xbox, Chromecast, Apple TV and more.', icon: 'tv' },
{ title: 'Watch anywhere', text: 'Stream on your phone, tablet or laptop — downloads available for offline viewing.', icon: 'devices' },
{ title: 'Personalized recommendations', text: 'Get suggestions based on what you watch to discover more you’ll love.', icon: 'recommend' },
{ title: 'Multiple profiles', text: 'Create profiles for family members with separate recommendations and settings.', icon: 'people' },
{ title: 'Ultra HD & HDR', text: 'Select titles available in 4K UHD and HDR for brilliant detail.', icon: '4k' },
{ title: 'Download & Go', text: 'Save shows to watch offline while commuting or traveling.', icon: 'download' },
{ title: 'Create watchlists', text: 'Save favorites to your list and pick up where you left off.', icon: 'playlist' },
{ title: 'Cancel anytime', text: 'No long-term commitment — pause or cancel your membership whenever you want.', icon: 'cancel' },
];
// FAQ 数据列表
faqs = [
{ question: 'What is Pudplanet?', answer: 'Pudplanet is a streaming service offering movies, TV shows, anime, documentaries and more across internet-connected devices. Enjoy unlimited ad‑free streaming for one monthly price.' },
{ question: 'How much does it cost?', answer: 'We offer several plans to fit different needs. Prices vary by region and plan (SD, HD, UHD). See the pricing page for current rates and available features.' },
{ question: 'Which devices are supported?', answer: 'Pudplanet works on Smart TVs, streaming players (Roku, Apple TV, Chromecast), game consoles, phones, tablets and web browsers.' },
{ question: 'Can I download shows to watch offline?', answer: 'Yes. Many titles are available for download on mobile apps so you can watch without an internet connection.' },
{ question: 'How do I cancel?', answer: 'You can cancel anytime from your account settings. Your subscription will remain active until the end of the current billing period.' },
{ question: 'Are there parental controls?', answer: 'Yes. You can create profiles and enable parental controls to restrict content by maturity rating.' },
{ question: 'What payment methods are accepted?', answer: 'We accept major credit/debit cards and selected local payment methods depending on your country.' },
];
}
4.2 模板文件:landing.html
路径:src/app/landing/landing.html
功能:定义落地页的 DOM 结构,绑定组件数据与事件。
主要区域包括:
- 背景层:全屏半透明黑色遮罩叠加背景图。
- 顶部导航栏:品牌 Logo 和“Sign In”按钮。
- 英雄区:主标题、副标题、邮箱输入表单及“Get Started”按钮。
- 特色理由网格:通过
*ngFor循环渲染reasons数组,展示功能亮点。 - FAQ 手风琴:使用 Angular Material 的
mat-expansion-panel展示常见问题,自定义展开/折叠图标。 - 页脚:版权信息、使用条款、隐私政策、帮助中心链接。
详细代码见你提供的 landing.html 文件。
4.3 样式文件:landing.css
路径:src/app/landing/landing.css
功能:为落地页提供深色主题的视觉样式,包括背景图叠加、玻璃态卡片、响应式布局、滚动条美化等。
- 背景图片路径为
/landing.jpg,实际文件应放置于public/目录下。 - 使用了 CSS 自定义属性(
--bg、--card等)定义主题色。 - 通过
clamp()函数实现字体和间距的流畅缩放。 - 包含对 Material 组件内部样式的深度覆盖(如
::ng-deep)。
💡 注意:由于 Angular 视图封装机制,需要使用
::ng-deep穿透组件样式以修改 Material 表单字段的边框颜色等。
五、项目运行与访问
- 启动开发服务器:在项目根目录执行
ng serve,默认访问http://localhost:4200。 - 页面路由:
/:落地页(Landing)/signup:注册页(需自行实现 Signup 组件)/login:尚未配置路由,点击“Sign In”会尝试跳转但无法匹配。
六、注意事项与已知问题
| 问题 | 说明 |
|---|---|
FormsModule 和 ReactiveFormsModule 未导入 | 在 shared-module.ts 中使用了这两个模块但未在文件顶部导入,导致 TypeScript 编译错误。需手动添加导入语句。 |
| 登录路由未实现 | Landing.login() 导航到 /login,但 AppRoutingModule 中该路由被注释,实际访问会重定向到首页。 |
| 背景图片路径 | CSS 中使用了 url('/landing.jpg'),请确保图片文件存在于 public/ 目录下,否则背景图不显示。 |
| Material 图标依赖 | 模板中使用了 <mat-icon>,需确保已在 index.html 中引入 Material Icons 字体库。 |
评论