跳到主要内容鸿蒙电商购物车全栈项目:用户管理、商品列表与购物车实现 | 极客日志TypeScript大前端
鸿蒙电商购物车全栈项目:用户管理、商品列表与购物车实现
本文基于鸿蒙生态构建电商购物车全栈项目,重点实现用户注册登录、信息管理、商品列表展示搜索及详情查看,并完整打通购物车添加、数量修改与删除功能。采用分层架构设计,通过工具类封装业务逻辑,结合 ArkTS 语言特性优化交互体验,确保数据流转安全高效,为后续订单支付模块奠定基础。
CoderByte0 浏览 鸿蒙电商购物车全栈项目:用户管理、商品列表与购物车实现
在构建完整的鸿蒙电商应用时,用户体系、商品展示与购物车逻辑是核心基石。本文将深入探讨如何基于 ArkTS 语言实现这三部分功能,采用分层架构设计,确保代码的可维护性与扩展性。
用户管理体系
用户管理不仅是注册登录的简单堆砌,更涉及权限控制与信息安全的平衡。我们采用分层架构,将业务逻辑、数据存储与界面渲染解耦。
服务层封装
为了保持页面代码的整洁,我们将用户相关的 API 调用封装在工具类中。以注册功能为例,UserRegistrationUtil 负责处理单例初始化与异步交互。
import user from '@ohos.user';
export class UserRegistrationUtil {
private static instance: UserRegistrationUtil | null = null;
private userHelper: user.UserHelper | null = null;
static getInstance(): UserRegistrationUtil {
if (!UserRegistrationUtil.instance) {
UserRegistrationUtil.instance = new UserRegistrationUtil();
}
return UserRegistrationUtil.instance;
}
async init(): Promise<void> {
if (!this.userHelper) {
this.userHelper = user.createUserHelper();
}
}
async (: , : ): <user.> {
(!.) ;
result = ..(email, password);
result;
}
(: ): <user.> {
(!.) ;
result = ..(email);
result;
}
}
register
email
string
password
string
Promise
UserRegistrationResult
if
this
userHelper
return
null
const
await
this
userHelper
register
return
async
sendVerificationCode
email
string
Promise
SendVerificationCodeResult
if
this
userHelper
return
null
const
await
this
userHelper
sendVerificationCode
return
这里使用单例模式是为了避免重复创建 Helper 实例,提升性能。实际开发中,建议增加异常捕获机制来处理网络波动或接口超时情况。
页面交互实现
在 RegistrationPage 中,我们利用状态管理组件绑定输入框数据,并通过按钮触发工具类方法。
import { UserRegistrationUtil } from '../utils/UserRegistrationUtil';
@Entry
@Component
struct RegistrationPage {
@State email: string = '';
@State password: string = '';
@State verificationCode: string = '';
build() {
Column({ space: 16 }) {
InputComponent({ placeholder: '请输入邮箱', value: this.email, onChange: (value: string) => { this.email = value; }, type: InputType.Email });
InputComponent({ placeholder: '请输入密码', value: this.password, onChange: (value: string) => { this.password = value; }, type: InputType.Password });
InputComponent({ placeholder: '请输入验证码', value: this.verificationCode, onChange: (value: string) => { this.verificationCode = value; }, type: InputType.Normal });
ButtonComponent({ text: '发送验证码', onClick: async () => { await this.sendVerificationCode(); }, disabled: !this.email });
ButtonComponent({ text: '注册', onClick: async () => { await this.register(); }, disabled: !this.email || !this.password || !this.verificationCode });
}.width('100%').height('100%').padding(16).backgroundColor('#F5F5F5');
}
aboutToAppear() {
UserRegistrationUtil.getInstance().init();
}
async sendVerificationCode(): Promise<void> {
const result = await UserRegistrationUtil.getInstance().sendVerificationCode(this.email);
if (result.success) {
promptAction.showToast({ message: '验证码发送成功' });
} else {
promptAction.showToast({ message: '验证码发送失败' });
}
}
async register(): Promise<void> {
const result = await UserRegistrationUtil.getInstance().register(this.email, this.password);
if (result.success) {
promptAction.showToast({ message: '注册成功' });
router.pushUrl({ url: '/pages/LoginPage' });
} else {
promptAction.showToast({ message: '注册失败' });
}
}
}
登录与信息管理的实现逻辑类似,重点在于状态同步与路由跳转的处理。修改密码时需校验旧密码,确保账户安全。
商品列表与详情
商品模块需要兼顾展示效率与搜索体验。通过 ProductListUtil 统一获取数据源,支持分页加载与关键词检索。
列表页设计
列表页不仅展示基础信息,还需预留点击跳转详情的入口。使用 ListComponent 配合自定义 renderItem 函数,可以灵活控制每个商品的布局。
import { ProductListUtil } from '../utils/ProductListUtil';
@Entry
@Component
struct ProductListPage {
@State productList: Array<product.Product> = [];
@State searchKeyword: string = '';
build() {
Column({ space: 16 }) {
InputComponent({ placeholder: '请输入搜索关键词', value: this.searchKeyword, onChange: (value: string) => { this.searchKeyword = value; }, type: InputType.Normal });
ButtonComponent({ text: '搜索', onClick: async () => { await this.searchProduct(); }, disabled: !this.searchKeyword });
ListComponent({ data: this.productList, renderItem: (item: product.Product, index: number) => {
Row({ space: 16 }) {
Image(item.avatarUrl).width(80).height(80).objectFit(ImageFit.Cover).borderRadius(8);
Column({ space: 8 }) {
Text(item.name).fontSize(16).fontWeight(FontWeight.Bold).textColor('#000000');
Text(item.description).fontSize(14).textColor('#666666').maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis });
Text(`¥${item.price}`).fontSize(16).fontWeight(FontWeight.Bold).textColor('#FF0000');
}.layoutWeight(1);
ButtonComponent({ text: '查看详情', onClick: () => { router.pushUrl({ url: '/pages/ProductDetailPage', params: { productId: item.productId } }); }, disabled: false });
}.width('100%').height('auto').padding(16).backgroundColor('#FFFFFF').borderRadius(8).margin({ bottom: 8 });
}, onItemClick: (item: product.Product, index: number) => { router.pushUrl({ url: '/pages/ProductDetailPage', params: { productId: item.productId } }); }});
}.width('100%').height('100%').padding(16).backgroundColor('#F5F5F5');
}
aboutToAppear() {
ProductListUtil.getInstance().init();
this.getProductList();
}
async getProductList(): Promise<void> {
this.productList = await ProductListUtil.getInstance().getProductList();
}
async searchProduct(): Promise<void> {
this.productList = await ProductListUtil.getInstance().searchProduct(this.searchKeyword);
}
}
详情页交互
详情页除了展示更多信息外,还承担着'加入购物车'的关键动作。数量选择器需限制最小值与最大值,防止非法数据提交。
import { ProductDetailUtil } from '../utils/ProductDetailUtil';
import { CartManagementUtil } from '../utils/CartManagementUtil';
@Entry
@Component
struct ProductDetailPage {
@State product: product.Product | null = null;
@State quantity: number = 1;
@State productId: number = 0;
build() {
Column({ space: 16 }) {
if (this.product) {
Image(this.product.avatarUrl).width('100%').height(240).objectFit(ImageFit.Cover).borderRadius(8);
Text(this.product.name).fontSize(18).fontWeight(FontWeight.Bold).textColor('#000000');
Text(this.product.description).fontSize(14).textColor('#666666').maxLines(5).textOverflow({ overflow: TextOverflow.Ellipsis });
Text(`¥${this.product.price}`).fontSize(18).fontWeight(FontWeight.Bold).textColor('#FF0000');
Row({ space: 16 }) {
Text('数量:').fontSize(14).textColor('#000000');
ButtonComponent({ text: '-', onClick: () => { if (this.quantity > 1) { this.quantity--; } }, disabled: this.quantity <= 1 });
Text(`${this.quantity}`).fontSize(14).textColor('#000000');
ButtonComponent({ text: '+', onClick: () => { this.quantity++; }, disabled: this.quantity >= 10 });
}.width('100%').height('auto').justifyContent(FlexAlign.Center);
ButtonComponent({ text: '添加到购物车', onClick: async () => { await this.addToCart(); }, disabled: !this.product });
}
}.width('100%').height('100%').padding(16).backgroundColor('#F5F5F5');
}
aboutToAppear() {
ProductDetailUtil.getInstance().init();
CartManagementUtil.getInstance().init();
this.getProductDetail();
}
async getProductDetail(): Promise<void> {
const params = router.getParams() as { productId: number };
this.productId = params.productId;
this.product = await ProductDetailUtil.getInstance().getProductDetail(this.productId);
}
async addToCart(): Promise<void> {
const result = await CartManagementUtil.getInstance().addToCart(this.productId, this.quantity);
if (result.success) {
promptAction.showToast({ message: '添加到购物车成功' });
} else {
promptAction.showToast({ message: '添加到购物车失败' });
}
}
}
购物车管理实战
购物车是电商流程中最复杂的环节之一,涉及库存校验、价格计算与状态同步。
核心逻辑
CartManagementUtil 提供了增删改查的基础能力。在实际项目中,建议增加本地缓存策略,防止网络请求失败导致数据丢失。
import cart from '@ohos.cart';
export class CartManagementUtil {
private static instance: CartManagementUtil | null = null;
private cartHelper: cart.CartHelper | null = null;
static getInstance(): CartManagementUtil {
if (!CartManagementUtil.instance) {
CartManagementUtil.instance = new CartManagementUtil();
}
return CartManagementUtil.instance;
}
async init(): Promise<void> {
if (!this.cartHelper) {
this.cartHelper = cart.createCartHelper();
}
}
async getCartList(): Promise<Array<cart.CartItem>> {
if (!this.cartHelper) return [];
const result = await this.cartHelper.getCartList();
return result;
}
async addToCart(productId: number, quantity: number): Promise<cart.AddToCartResult> {
if (!this.cartHelper) return null;
const result = await this.cartHelper.addToCart(productId, quantity);
return result;
}
async modifyCartItemQuantity(cartItemId: number, quantity: number): Promise<cart.ModifyCartItemQuantityResult> {
if (!this.cartHelper) return null;
const result = await this.cartHelper.modifyCartItemQuantity(cartItemId, quantity);
return result;
}
async deleteCartItem(cartItemId: number): Promise<cart.DeleteCartItemResult> {
if (!this.cartHelper) return null;
const result = await this.cartHelper.deleteCartItem(cartItemId);
return result;
}
async clearCart(): Promise<cart.ClearCartResult> {
if (!this.cartHelper) return null;
const result = await this.cartHelper.clearCart();
return result;
}
}
购物车页面
页面端需实时响应数量变化,并自动刷新总价(此处简化为刷新列表)。删除操作通常建议二次确认,避免误触。
import { CartManagementUtil } from '../utils/CartManagementUtil';
@Entry
@Component
struct CartPage {
@State cartList: Array<cart.CartItem> = [];
build() {
Column({ space: 16 }) {
ListComponent({ data: this.cartList, renderItem: (item: cart.CartItem, index: number) => {
Row({ space: 16 }) {
Image(item.avatarUrl).width(80).height(80).objectFit(ImageFit.Cover).borderRadius(8);
Column({ space: 8 }) {
Text(item.name).fontSize(16).fontWeight(FontWeight.Bold).textColor('#000000');
Text(item.description).fontSize(14).textColor('#666666').maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis });
Text(`¥${item.price}`).fontSize(16).fontWeight(FontWeight.Bold).textColor('#FF0000');
}.layoutWeight(1);
Row({ space: 8 }) {
ButtonComponent({ text: '-', onClick: async () => { await this.modifyCartItemQuantity(item.cartItemId, item.quantity - 1); }, disabled: item.quantity <= 1 });
Text(`${item.quantity}`).fontSize(14).textColor('#000000');
ButtonComponent({ text: '+', onClick: async () => { await this.modifyCartItemQuantity(item.cartItemId, item.quantity + 1); }, disabled: item.quantity >= 10 });
}.width('auto').height('auto');
ButtonComponent({ text: '删除', onClick: async () => { await this.deleteCartItem(item.cartItemId); }, disabled: false });
}.width('100%').height('auto').padding(16).backgroundColor('#FFFFFF').borderRadius(8).margin({ bottom: 8 });
}, onItemClick: (item: cart.CartItem, index: number) => { router.pushUrl({ url: '/pages/ProductDetailPage', params: { productId: item.productId } }); }});
ButtonComponent({ text: '清空购物车', onClick: async () => { await this.clearCart(); }, disabled: this.cartList.length === 0 });
}.width('100%').height('100%').padding(16).backgroundColor('#F5F5F5');
}
aboutToAppear() {
CartManagementUtil.getInstance().init();
this.getCartList();
}
async getCartList(): Promise<void> {
this.cartList = await CartManagementUtil.getInstance().getCartList();
}
async modifyCartItemQuantity(cartItemId: number, quantity: number): Promise<void> {
const result = await CartManagementUtil.getInstance().modifyCartItemQuantity(cartItemId, quantity);
if (result.success) {
this.getCartList();
} else {
promptAction.showToast({ message: '修改购物车商品数量失败' });
}
}
async deleteCartItem(cartItemId: number): Promise<void> {
const result = await CartManagementUtil.getInstance().deleteCartItem(cartItemId);
if (result.success) {
this.getCartList();
} else {
promptAction.showToast({ message: '删除购物车商品失败' });
}
}
async clearCart(): Promise<void> {
const result = await CartManagementUtil.getInstance().clearCart();
if (result.success) {
this.getCartList();
} else {
promptAction.showToast({ message: '清空购物车失败' });
}
}
}
部署与验证
完成编码后,需在 DevEco Studio 中进行编译打包。记得在 module.json5 中配置必要的权限声明,特别是涉及用户数据与网络访问的部分。
- 未登录状态下添加商品是否拦截?
- 库存不足时能否加入购物车?
- 弱网环境下列表加载是否有友好提示?
通过上述步骤,我们完成了用户、商品与购物车三大核心模块的闭环。后续可在此基础上扩展订单支付与物流追踪功能,逐步完善整个电商生态。
相关免费在线工具
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
- JSON美化和格式化
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online