Ionic电商应用实战 #
一、项目概述 #
1.1 功能需求 #
text
电商应用功能
│
├── 首页
│ ├── 轮播图
│ ├── 商品分类
│ └── 推荐商品
│
├── 商品
│ ├── 商品列表
│ ├── 商品详情
│ └── 商品评价
│
├── 购物车
│ ├── 商品管理
│ ├── 数量调整
│ └── 结算
│
├── 订单
│ ├── 订单列表
│ ├── 订单详情
│ └── 物流跟踪
│
└── 我的
├── 个人信息
├── 收货地址
└── 设置
二、数据模型 #
2.1 商品模型 #
typescript
// models/product.model.ts
export interface Product {
id: string;
name: string;
description: string;
price: number;
originalPrice: number;
discount: number;
images: string[];
mainImage: string;
category: Category;
stock: number;
sales: number;
rating: number;
reviews: Review[];
specifications: Specification[];
tags: string[];
}
export interface Category {
id: string;
name: string;
icon: string;
children?: Category[];
}
export interface Specification {
name: string;
value: string;
}
export interface Review {
id: string;
userId: string;
userName: string;
userAvatar: string;
rating: number;
content: string;
images: string[];
createTime: Date;
}
2.2 购物车模型 #
typescript
// models/cart.model.ts
export interface CartItem {
id: string;
product: Product;
quantity: number;
selected: boolean;
sku?: string;
}
export interface Cart {
items: CartItem[];
totalPrice: number;
totalQuantity: number;
selectedItems: CartItem[];
}
2.3 订单模型 #
typescript
// models/order.model.ts
export interface Order {
id: string;
orderNo: string;
status: OrderStatus;
items: OrderItem[];
totalPrice: number;
discount: number;
actualPrice: number;
address: Address;
paymentMethod: string;
paymentTime?: Date;
deliveryTime?: Date;
createTime: Date;
logistics?: Logistics;
}
export enum OrderStatus {
Pending = 'pending',
Paid = 'paid',
Shipped = 'shipped',
Delivered = 'delivered',
Cancelled = 'cancelled'
}
export interface OrderItem {
productId: string;
productName: string;
productImage: string;
price: number;
quantity: number;
}
export interface Address {
id: string;
name: string;
phone: string;
province: string;
city: string;
district: string;
detail: string;
isDefault: boolean;
}
export interface Logistics {
company: string;
trackingNo: string;
traces: LogisticsTrace[];
}
export interface LogisticsTrace {
time: Date;
description: string;
}
三、购物车服务 #
3.1 购物车服务 #
typescript
// services/cart.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { Cart, CartItem } from '../models/cart.model';
import { Product } from '../models/product.model';
import { StorageService } from './storage.service';
@Injectable({
providedIn: 'root'
})
export class CartService {
private cartSubject = new BehaviorSubject<Cart>(this.getEmptyCart());
cart$ = this.cartSubject.asObservable();
constructor(private storage: StorageService) {
this.loadCart();
}
private getEmptyCart(): Cart {
return {
items: [],
totalPrice: 0,
totalQuantity: 0,
selectedItems: []
};
}
private loadCart() {
const savedCart = this.storage.get<Cart>('cart');
if (savedCart) {
this.cartSubject.next(savedCart);
}
}
private saveCart(cart: Cart) {
this.storage.set('cart', cart);
this.cartSubject.next(cart);
}
private calculateTotals(cart: Cart): Cart {
const selectedItems = cart.items.filter(item => item.selected);
return {
...cart,
totalPrice: selectedItems.reduce((sum, item) => sum + item.product.price * item.quantity, 0),
totalQuantity: cart.items.reduce((sum, item) => sum + item.quantity, 0),
selectedItems
};
}
addToCart(product: Product, quantity: number = 1) {
const cart = this.cartSubject.value;
const existingItem = cart.items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
cart.items.push({
id: Date.now().toString(),
product,
quantity,
selected: true
});
}
this.saveCart(this.calculateTotals(cart));
}
removeFromCart(itemId: string) {
const cart = this.cartSubject.value;
cart.items = cart.items.filter(item => item.id !== itemId);
this.saveCart(this.calculateTotals(cart));
}
updateQuantity(itemId: string, quantity: number) {
const cart = this.cartSubject.value;
const item = cart.items.find(i => i.id === itemId);
if (item) {
item.quantity = Math.max(1, quantity);
this.saveCart(this.calculateTotals(cart));
}
}
toggleSelection(itemId: string) {
const cart = this.cartSubject.value;
const item = cart.items.find(i => i.id === itemId);
if (item) {
item.selected = !item.selected;
this.saveCart(this.calculateTotals(cart));
}
}
selectAll(selected: boolean) {
const cart = this.cartSubject.value;
cart.items.forEach(item => item.selected = selected);
this.saveCart(this.calculateTotals(cart));
}
clearCart() {
this.saveCart(this.getEmptyCart());
}
clearSelected() {
const cart = this.cartSubject.value;
cart.items = cart.items.filter(item => !item.selected);
this.saveCart(this.calculateTotals(cart));
}
}
四、购物车页面 #
4.1 购物车页面 #
typescript
// features/cart/cart.page.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Cart, CartItem } from '../../models/cart.model';
import { CartService } from '../../services/cart.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-cart',
templateUrl: 'cart.page.html',
styleUrls: ['cart.page.scss']
})
export class CartPage implements OnInit {
cart$: Observable<Cart>;
constructor(
private cartService: CartService,
private router: Router
) {
this.cart$ = this.cartService.cart$;
}
ngOnInit() {}
increaseQuantity(item: CartItem) {
this.cartService.updateQuantity(item.id, item.quantity + 1);
}
decreaseQuantity(item: CartItem) {
if (item.quantity > 1) {
this.cartService.updateQuantity(item.id, item.quantity - 1);
}
}
removeItem(item: CartItem) {
this.cartService.removeFromCart(item.id);
}
toggleSelection(item: CartItem) {
this.cartService.toggleSelection(item.id);
}
selectAll(selected: boolean) {
this.cartService.selectAll(selected);
}
checkout() {
this.router.navigate(['/checkout']);
}
}
html
<!-- features/cart/cart.page.html -->
<ion-header>
<ion-toolbar>
<ion-title>购物车</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ng-container *ngIf="cart$ | async as cart">
<ion-list *ngIf="cart.items.length > 0; else emptyCart">
<ion-item *ngFor="let item of cart.items">
<ion-checkbox
slot="start"
[checked]="item.selected"
(ionChange)="toggleSelection(item)">
</ion-checkbox>
<ion-thumbnail slot="start">
<ion-img [src]="item.product.mainImage"></ion-img>
</ion-thumbnail>
<ion-label>
<h2>{{ item.product.name }}</h2>
<p>¥{{ item.product.price }}</p>
<ion-buttons>
<ion-button (click)="decreaseQuantity(item)">
<ion-icon name="remove"></ion-icon>
</ion-button>
<ion-label>{{ item.quantity }}</ion-label>
<ion-button (click)="increaseQuantity(item)">
<ion-icon name="add"></ion-icon>
</ion-button>
</ion-buttons>
</ion-label>
<ion-button slot="end" fill="clear" color="danger" (click)="removeItem(item)">
<ion-icon name="trash"></ion-icon>
</ion-button>
</ion-item>
</ion-list>
<ng-template #emptyCart>
<div class="empty-cart">
<ion-icon name="cart-outline"></ion-icon>
<p>购物车是空的</p>
<ion-button routerLink="/tabs/home">去购物</ion-button>
</div>
</ng-template>
</ng-container>
</ion-content>
<ion-footer>
<ion-toolbar>
<ion-checkbox slot="start" (ionChange)="selectAll($event.detail.checked)">全选</ion-checkbox>
<ion-title slot="start">
合计: ¥{{ (cart$ | async)?.totalPrice }}
</ion-title>
<ion-button slot="end" (click)="checkout()">结算</ion-button>
</ion-toolbar>
</ion-footer>
五、订单服务 #
5.1 订单服务 #
typescript
// services/order.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { ApiService } from './api.service';
import { Order, OrderStatus } from '../models/order.model';
@Injectable({
providedIn: 'root'
})
export class OrderService {
constructor(private api: ApiService) {}
createOrder(data: any): Observable<Order> {
return this.api.post<Order>('/orders', data);
}
getOrders(status?: OrderStatus): Observable<Order[]> {
const params = status ? { status } : {};
return this.api.get<Order[]>('/orders', params);
}
getOrder(id: string): Observable<Order> {
return this.api.get<Order>(`/orders/${id}`);
}
cancelOrder(id: string): Observable<void> {
return this.api.post<void>(`/orders/${id}/cancel`, {});
}
confirmReceive(id: string): Observable<void> {
return this.api.post<void>(`/orders/${id}/confirm`, {});
}
getLogistics(id: string): Observable<any> {
return this.api.get(`/orders/${id}/logistics`);
}
}
六、支付集成 #
6.1 支付服务 #
typescript
// services/payment.service.ts
import { Injectable } from '@angular/core';
import { ApiService } from './api.service';
export type PaymentMethod = 'alipay' | 'wechat' | 'balance';
@Injectable({
providedIn: 'root'
})
export class PaymentService {
constructor(private api: ApiService) {}
async pay(orderId: string, method: PaymentMethod): Promise<boolean> {
try {
const result = await this.api.post<any>(`/payment/${method}`, { orderId }).toPromise();
if (method === 'alipay') {
return this.handleAlipay(result.payUrl);
} else if (method === 'wechat') {
return this.handleWechatPay(result);
}
return true;
} catch (error) {
console.error('支付失败:', error);
return false;
}
}
private async handleAlipay(payUrl: string): Promise<boolean> {
// 调用支付宝支付
window.location.href = payUrl;
return true;
}
private async handleWechatPay(params: any): Promise<boolean> {
// 调用微信支付
return true;
}
}
七、最佳实践 #
7.1 性能优化 #
- 使用虚拟滚动处理长列表
- 图片懒加载
- 路由懒加载
- 状态管理优化
7.2 安全考虑 #
- 支付安全
- 数据加密
- 接口安全
八、总结 #
8.1 项目要点 #
| 要点 | 说明 |
|---|---|
| 购物车 | 本地存储 + 状态管理 |
| 订单 | API + 状态管理 |
| 支付 | 第三方支付集成 |
| 物流 | 物流跟踪API |
8.2 下一步 #
完成了电商应用后,继续学习 社交应用,掌握更多应用开发技巧!
最后更新:2026-03-28