Imitation Box Horse - Product Details Page (10)
Technical Stack
Appgallery Connect
Development Preparation In the previous section, we implemented a custom title bar and data reception for the product details page. This section focuses on enriching the product details page with comprehensive information, including product attributes, images, specifications, promotion details, and fixed bottom actions (Add to Cart and Buy Now) with real-time cart updates.
Functional Analysis Layout Requirements: Scrollable content area for extensive product information. Fixed bottom section with Add to Cart and Buy Now buttons. Real-time cart item count update upon adding products. Quick navigation to the cart page.
Interactive Features: Specification selection (size, color, quantity). Promotion display (discounts, purchase limits). Smooth scrolling and responsive UI.
Code Implementation
- Enhanced Product Details Page (ProductDetailsPage.ets) typescript import { CommonTopBar } from '../widget/CommonTopBar'; import router from '@ohos.router'; import { HomeProductList } from '../entity/home_product_list'; import promptAction from '@ohos.promptAction';
@Entry @Component struct ProductDetailsPage { @State productParams: HomeProductList = {} as HomeProductList; @State selectedSpec: string = 'Default'; @State cartCount: number = 0; private scroller: Scroller = new Scroller();
aboutToAppear(): void { const params = router.getParams() as HomeProductList; if (params) { this.productParams = params; // Simulate cart count from local storage or global state this.cartCount = 2; // Replace with actual cart data retrieval } }
// Add product to cart addToCart() { if (!this.selectedSpec || this.selectedSpec === 'Default') { promptAction.showToast({ message: 'Please select a specification', duration: 2000 }); return; }
// Update cart count (simulation) this.cartCount += 1; promptAction.showToast({ message: 'Added to cart', duration: 2000 });
// Save to local storage or update global cart state // e.g., storage.set('cartCount', this.cartCount); }
// Navigate to cart page goToCart() { router.pushUrl({ url: 'pages/CartPage' }); }
build() { Stack({ alignContent: Alignment.Bottom }) { Scroll(this.scroller) { Column() { CommonTopBar({ title: this.productParams.name || "Product Details", alpha: 0, titleAlignment: TextAlign.Center, backButton: true });
// Product image gallery Image(this.productParams.url || 'https://via.placeholder.com/750x400') .width('100%') .height(300) .objectFit(ImageFit.Cover);
Column({ space: 10 }) { // Price section Row() { if (this.productParams.promotion_spread_price > 0) { Text() { Span("¥") .fontSize(14) .fontColor(Color.Red); Span((this.productParams.price - this.productParams.promotion_spread_price).toString()) .fontSize(20) .fontColor(Color.Red); } } else { Text() { Span("¥") .fontSize(14) .fontColor(Color.Red); Span(this.productParams.price.toString()) .fontSize(20) .fontColor(Color.Red); } }
Text("¥" + this.productParams.original_price) .fontColor('#999') .decoration({ type: TextDecorationType.LineThrough, color: Color.Gray }) .fontSize(16) .margin({ left: 10 });
if (this.productParams.promotion_spread_price > 0) { Row() { Text("Save ¥" + this.productParams.promotion_spread_price) .fontSize(14) .backgroundColor("#FB424C") .borderRadius(20) .padding(3) .textAlign(TextAlign.Center); Text("Limit " + this.productParams.max_loop_amount + " per person") .margin({ left: 5 }) .fontSize(14) .backgroundColor("#FB424C") .borderRadius(20) .padding(3) .textAlign(TextAlign.Center); } .padding({ top: 2, bottom: 2, left: 10 }); } } .padding(10);
if (this.productParams.promotion_spread_price > 0) { Text(this.productParams.endTime) .fontSize(14) .backgroundColor("#FB424C") .borderRadius(20) .padding(3) .margin({ left: 10 }) .textAlign(TextAlign.Center); }
// Product name and details Text(this.productParams.name || 'Product Name') .fontSize(20) .fontColor(Color.Black) .margin({ left: 10 }) .fontWeight(FontWeight.Bold); Text(this.productParams.text_message || 'Product description') .fontSize(14) .fontColor(Color.Black) .margin({ left: 10 }); Row() { Text("Sales: " + this.productParams.sales_volume) .fontSize(14) .fontColor(Color.Black); } .padding(10) .width('100%') .justifyContent(FlexAlign.Start);
// Delivery information Divider().width('100%').height(5).backgroundColor("#f7f7f7"); Row() { Text("Delivery") .fontColor(Color.Gray) .fontSize(14); Text(this.productParams.delivery_time || 'Shipped within 24h') .margin({ left: 20 }) .fontSize(14) .fontColor(Color.Black); } .padding(10) .width('100%') .justifyContent(FlexAlign.Start);
// Product parameters Divider().width('100%').height(5).backgroundColor("#f7f7f7"); Row() { Text("Parameters") .fontColor(Color.Gray) .fontSize(14); Text("Storage: ") .margin({ left: 20 }) .fontSize(14) .fontColor(Color.Black); Text(this.productParams.parameter || 'Refrigerated') .fontSize(14) .fontColor(Color.Black); } .padding(10) .width('100%') .justifyContent(FlexAlign.Start);
// Specification selection Divider().width('100%').height(5).backgroundColor("#f7f7f7"); Row() { Text("Specifications") .fontColor(Color.Gray) .fontSize(14); Text(this.selectedSpec || 'Please select a specification') .margin({ left: 20 }) .fontSize(14) .fontColor(Color.Black); } .padding(10) .width('100%') .justifyContent(FlexAlign.Start);
// Sample specification options (replace with dynamic data) Divider().width('100%').height(5).backgroundColor("#f7f7f7"); Text("Available Specifications") .fontSize(16) .fontWeight(FontWeight.Bold) .margin({ left: 10, top: 5, bottom: 5 }); Row() { // Sample specifications (e.g., size, color) ForEach(['500g', '1kg', '2kg'], (spec: string) => { Text(spec) .padding(8) .backgroundColor(this.selectedSpec === spec ? '#FB424C' : '#F0F0F0') .fontColor(this.selectedSpec === spec ? Color.White : Color.Black) .borderRadius(8) .onClick(() => this.selectedSpec = spec); }) } .padding(10) .width('100%') .justifyContent(FlexAlign.Start) .spacing(10); } .alignItems(HorizontalAlign.Start) .width('100%'); } .alignItems(HorizontalAlign.Start) .backgroundColor(Color.White); } .padding({ bottom: 80 }) .height('100%') .width('100%');
// Fixed bottom action bar Row() { Stack() { Image($r('app.media.shopping_cart')) .width(35) .height(35) .objectFit(ImageFit.Contain); if (this.cartCount > 0) { Text(this.cartCount.toString()) .fontSize(10) .backgroundColor(Color.Red) .borderRadius(10) .padding(4) .position({ x: 20, y: -10 }); } } .onClick(() => this.goToCart()) .width(50) .height(50);
Blank();
Text("Add to Cart") .padding(10) .width(100) .textAlign(TextAlign.Center) .backgroundColor("#FCDB29") .fontColor(Color.White) .borderRadius({ topLeft: 15, bottomLeft: 15 }) .onClick(() => this.addToCart());
Text("Buy Now") .padding(10) .textAlign(TextAlign.Center) .width(100) .backgroundColor(Color.Red) .fontColor(Color.White) .borderRadius({ topRight: 15, bottomRight: 15 }) .onClick(() => { if (this.selectedSpec === 'Default') { promptAction.showToast({ message: 'Please select a specification', duration: 2000 }); return; } // Navigate to checkout router.pushUrl({ url: 'pages/CheckoutPage', params: { ...this.productParams, spec: this.selectedSpec } }); }); } .padding(15) .justifyContent(FlexAlign.SpaceBetween) .width('100%') .backgroundColor(Color.White) .shadow({ radius: 2, color: '#EEEEEE', offsetX: 0, offsetY: -1 }); } .backgroundColor(Color.White); } }
This implementation provides a comprehensive product details page with essential e-commerce features, ready for further integration with backend services and user authentication.