本文會通過ETS實作一個購物車應用
1. 建立商品清單頁面
在這一節,我們将完成首頁界面以及商品清單頁簽的設計,效果圖如下:
從上面效果圖可以看出,主界面商品清單頁簽主要由下面三個部分組成:
(1)頂部的Tabs元件。
(2)中間TabContent元件内包含List元件。其中List元件的item是一個水準布局,由一個垂直布局和一個Image元件組成;item中的垂直布局由3個Text元件組成。
(3)底部的頁簽導航。
下面來一起建立第一個頁面,第一個頁面的檔案是pages/HomePage.ets,現在先建立該檔案。
在與pages檔案夾同級目錄建立一個model檔案夾,并在model目錄下建立ArsData.ets、GoodsData.ets、Menu.ets和GoodsDataModels.ets檔案,其中ArsData.ets、GoodsData.ets、Menu.ets是資料實體類,GoodsDataModels.ets是存放這三種實體資料集合,并定義了擷取各種資料集合的方法。資料實體包含實體的屬性和構造方法,可通過new ArsData(string,string) 來擷取ArsData對象,ArsData.ets内容如下:
let NextId = 0;
export class ArsData {
id: string;
title: string;
content: string;
constructor(title: string, content: string) {
this.id = `${NextId++}`;
this.title = title;
this.content = content;
}
}
GoodsData.ets代碼如下:
let NextId = 0;
export class GoodsData {
id: string;
title: string;
content: string;
price: number;
imgSrc: Resource;
constructor(title: string, content: string, price: number, imgSrc: Resource) {
this.id = `${NextId++}`;
this.title = title;
this.content = content;
this.price = price;
this.imgSrc = imgSrc;
}
}
一個檔案中可以包含多個class ,Menu.ets中就包含了Menu類和ImageItem類,Menu.ets代碼如下:
let NextId = 0;
export class Menu {
id: string;
title: string;
num: number;
constructor(title: string, num: number) {
this.id = `${NextId++}`;
this.title = title;
this.num = num;
}
}
export class ImageItem {
id: string;
title: string;
imageSrc: Resource;
constructor(title: string, imageSrc: Resource) {
this.id = `${NextId++}`;
this.title = title;
this.imageSrc = imageSrc;
}
}
GoodsDataModels.ets代碼如下:
import {GoodsData} from './GoodsData'
import {Menu, ImageItem} from './Menu'
import {ArsData} from './ArsData'
// 擷取商品清單資料
export function initializeOnStartup(): Array<GoodsData> {
let GoodsDataArray: Array<GoodsData> = []
GoodsComposition.forEach(item => {
console.log(item.title);
GoodsDataArray.push(new GoodsData(item.title, item.content, item.price, item.imgSrc));
})
return GoodsDataArray;
}
// 擷取底部預設圖檔清單資料
export function getIconPath(): Array<string> {
let IconPath: Array<string> = ['nav/icon-buy.png','nav/icon-shopping-cart.png','nav/icon-my.png']
return IconPath;
}
// 擷取選中後圖檔清單資料
export function getIconPathSelect(): Array<string> {
let IconPathSelect: Array<string> = ['nav/icon-home.png','nav/icon-shopping-cart-select.png','nav/icon-my-select.png']
return IconPathSelect;
}
// 擷取商品詳情頁圖檔詳情清單
export function getDetailImages(): Array<string> {
let detailImages: Array<string> = ['computer/computer1.png','computer/computer2.png','computer/computer3.png','computer/computer4.png','computer/computer5.png','computer/computer6.png']
return detailImages;
}
// 擷取菜單資料清單
export function getMenu(): Array<Menu> {
let MenuArray: Array<Menu> = []
MyMenu.forEach(item => {
MenuArray.push(new Menu(item.title,item.num));
})
return MenuArray;
}
// 擷取MyTrans資料清單
export function getTrans(): Array<ImageItem> {
let ImageItemArray: Array<ImageItem> = []
MyTrans.forEach(item => {
ImageItemArray.push(new ImageItem(item.title,item.imageSrc));
})
return ImageItemArray;
}
// 擷取More資料清單
export function getMore(): Array<ImageItem> {
let ImageItemArray: Array<ImageItem> = []
MyMore.forEach(item => {
ImageItemArray.push(new ImageItem(item.title,item.imageSrc));
})
return ImageItemArray;
}
// 擷取參數清單
export function getArs(): Array<ArsData> {
let ArsItemArray: Array<ArsData> = []
ArsList.forEach(item => {
ArsItemArray.push(new ArsData(item.title,item.content));
})
return ArsItemArray;
}
在HomePage.ets檔案中建立商品清單頁簽相關的元件,其中GoodsHome效果圖如下:
代碼如下:
@Component
struct GoodsHome {
private goodsItems: GoodsData[]
build() {
Column() {
Tabs() {
TabContent() {
GoodsList({ goodsItems: this.goodsItems });
}
.tabBar("Top Sellers")
.backgroundColor(Color.White)
TabContent() {
GoodsList({ goodsItems: this.goodsItems });
}
.tabBar("Recommended")
.backgroundColor(Color.White)
TabContent() {
GoodsList({ goodsItems: this.goodsItems });
}
.tabBar("Lifestyle")
.backgroundColor(Color.White)
TabContent() {
GoodsList({ goodsItems: this.goodsItems });
}
.tabBar("Deals")
.backgroundColor(Color.White)
}
.barWidth(500)
.barHeight(25)
.scrollable(true)
.barMode(BarMode.Scrollable)
.backgroundColor('#007DFF')
.height(630)
}
.alignItems(HorizontalAlign.Start)
.width('100%')
}
}
在GoodsHome中使用Tabs元件,在Tabs元件中設定4個TabContent,給每個TabContent設定tabBar屬性,并設定TabContent容器中的内容GoodsList元件,GoodsList元件的代碼如下:
@Component
struct GoodsList {
private goodsItems: GoodsData[]
build() {
Column() {
List() {
ForEach(this.goodsItems, item => {
ListItem() {
GoodsListItem({ goodsItem: item })
}
}, item => item.id.toString())
}
.height('100%')
.width('100%')
.align(Alignment.Top)
.margin({top: 5})
}
}
}
2. 建立購物車頁面
購物車主界面如下:
從上面效果圖可以看出,主界面購物車頁簽主要由下面三部分組成:
(1)頂部的Text元件。
(2)中間的List元件,其中List元件的item是一個水準的布局内包含一個toggle元件,一個Image元件和一個垂直布局,其item中的垂直布局是由2個Text元件組成。
(3)底部一個水準布局包含兩個Text元件。
import {GoodsData} from '../model/GoodsData'
import {initializeOnStartup} from '../model/GoodsDataModels'
import prompt from '@system.prompt';
@Entry
@Component
export struct ShoppingCart {
@Provide totalPrice : number =0
private goodsItems: GoodsData[] = initializeOnStartup()
build() {
Column() {
Column() {
Text('ShoppingCart')
.fontColor(Color.Black)
.fontSize(25)
.margin({ left: 60,right:60 })
.align(Alignment.Center)
}
.backgroundColor('#FF00BFFF')
.width('100%')
.height(30)
ShopCartList({ goodsItems: this.goodsItems });
ShopCartBottom()
}
.alignItems(HorizontalAlign.Start)
}
}
建立ShopCartList元件用于存放購物車商品清單,ShopCartList元件的代碼如下:
@Component
struct ShopCartList {
private goodsItems: GoodsData[]
build() {
Column() {
List() {
ForEach(this.goodsItems, item => {
ListItem() {
ShopCartListItem({ goodsItem: item })
}
}, item => item.id.toString())
}
.height('100%')
.width('100%')
.align(Alignment.Top)
.margin({top: 5})
}
.height(570)
}
}
ShopCartListItem元件效果圖如下:
代碼如下:
@Component
struct ShopCartListItem {
@Consume totalPrice: number
private goodsItem: GoodsData
build() {
Row() {
Toggle({type: ToggleType.Checkbox})
.width(10)
.height(10)
.onChange((isOn:boolean) => {
if(isOn){
this.totalPrice +=parseInt(this.goodsItem.price+'',0)
}else{
this.totalPrice -= parseInt(this.goodsItem.price+'',0)
}
})
Image(this.goodsItem.imgSrc)
.objectFit(ImageFit.ScaleDown)
.height(100)
.width(100)
.renderMode(ImageRenderMode.Original)
Column() {
Text(this.goodsItem.title)
.fontSize(14)
Text('¥' + this.goodsItem.price)
.fontSize(14)
.fontColor(Color.Red)
}
}
.height(100)
.width(180)
.margin({left: 20})
.alignItems(VerticalAlign.Center)
.backgroundColor(Color.White)
}
}
建立ShopCartBottom元件,ShopCartBottom元件效果圖如下:
@Component
struct ShopCartBottom {
@Consume totalPrice: number
build() {
Row() {
Text('Total: ¥'+this.totalPrice)
.fontColor(Color.Red)
.fontSize(18)
.margin({left:20})
.width(150)
Text('Check Out')
.fontColor(Color.Black)
.fontSize(18)
.margin({right:20,left:100})
.onClick(()=>{
prompt.showToast({
message: 'Checking Out',
duration: 10,
bottom:100
})
})
}
.height(30)
.width('100%')
.backgroundColor('#FF7FFFD4')
.alignItems(VerticalAlign.Bottom)
}
}
3. 建構商品詳情頁
商品詳情頁的效果如下圖所示:
從上面效果圖可以看出,商品詳情頁面主要由下面五部分組成:
(1)頂部的傳回欄。
(2)Swiper元件。
(3)中間多個Text元件組成的布局。
(4)參數清單。
(5)底部的Buy。
在本節中,把上面每一部分都封裝成一個元件,然後再放到入口元件内,當點選頂部傳回圖示時傳回到首頁面的商品清單頁簽,點選底部Buy時,會觸發進度條彈窗
1. 在pages目錄下面建立一個eTS Page, 命名為ShoppingDetail.ets,config.json檔案pages屬性中也會自動添加"pages/ShoppingDetail"頁面路由。
2. 在ShoppingDetail.ets檔案中建立入口元件,元件内容如下:
@Entry
@Component
struct ShoppingDetail {
private arsItems: ArsData[] = getArs()
private detailImages: string[] = getDetailImages()
build() {
Column() {
DetailTop()
Scroll() {
Column() {
SwiperTop()
DetailText()
DetailArsList({ arsItems: this.arsItems })
Image($rawfile('computer/computer1.png'))
.height(220)
.width('100%')
.margin({top:30})
Image($rawfile('computer/computer2.png'))
.height(220)
.width('100%')
.margin({top:30})
Image($rawfile('computer/computer3.png'))
.height(220)
.width('100%')
.margin({top:30})
Image($rawfile('computer/computer4.png'))
.height(220)
.width('100%')
.margin({top:30})
Image($rawfile('computer/computer5.png'))
.height(220)
.width('100%')
.margin({top:30})
Image($rawfile('computer/computer6.png'))
.height(220)
.width('100%')
.margin({top:30})
}
.width('100%')
.flexGrow(1)
}
.scrollable(ScrollDirection.Vertical)
DetailBottom()
}
.height(630)
}
}
其中頂部DetailTop元件效果圖如下:
代碼如下:
@Component
struct DetailTop{
build(){
Column(){
Row(){
Image($rawfile('detail/icon-return.png'))
.height(20)
.width(20)
.margin({ left: 20, right: 250 })
.onClick(() => {
router.push({
uri: "pages/HomePage"
})
})
}
.width('100%')
.height(25)
.backgroundColor('#FF87CEEB')
}
.width('100%')
.height(30)
}
}
SwiperTop元件效果圖如下:
代碼如下:
@Component
struct SwiperTop{
build() {
Column() {
Swiper() {
Image($rawfile('computer/computer1.png'))
.height(220)
.width('100%')
Image($rawfile('computer/computer2.png'))
.height(220)
.width('100%')
Image($rawfile('computer/computer3.png'))
.height(220)
.width('100%')
Image($rawfile('computer/computer4.png'))
.height(220)
.width('100%')
Image($rawfile('computer/computer5.png'))
.height(220)
.width('100%')
Image($rawfile('computer/computer6.png'))
.height(220)
.width('100%')
}
.index(0)
.autoPlay(true)
.interval(3000)
.indicator(true)
.loop(true)
.height(250)
.width('100%')
}
.height(250)
.width('100%')
}
}
DetailText元件效果圖如下:
代碼如下:
@Component
struct DetailText{
build() {
Column() {
Row(){
Image($rawfile('computer/icon-promotion.png'))
.height(30)
.width(30)
.margin({left:10})
Text('Special Offer: ¥9999')
.fontColor(Color.White)
.fontSize(20)
.margin({left:10})
}
.width('100%')
.height(35)
.backgroundColor(Color.Red)
Column(){
Text('New Arrival: HUAWEI MateBook X Pro 2021')
.fontSize(15)
.margin({left:10})
.alignSelf(ItemAlign.Start)
Text('13.9-Inch, 11th Gen Intel® Core™ i7, 16 GB of Memory, 512 GB of Storage, Ultra-slim Business Laptop, 3K FullView Display, Multi-screen Collaboration, Emerald Green')
.fontSize(10)
.margin({left:10})
Row(){
Image($rawfile('nav/icon-buy.png'))
.height(15)
.width(15)
.margin({left:10})
Text('Limited offer')
.fontSize(10)
.fontColor(Color.Red)
.margin({left:100})
}
.backgroundColor(Color.Pink)
.width('100%')
.height(25)
.margin({top:10})
Text(' Shipment: 2-day shipping')
.fontSize(13)
.fontColor(Color.Red)
.margin({left:10,top:5})
.alignSelf(ItemAlign.Start)
Text(' Ship To: Hubei,Wuhan,China')
.fontSize(13)
.fontColor(Color.Red)
.margin({left:10,top:5})
.alignSelf(ItemAlign.Start)
.onClick(()=>{
prompt.showDialog({title:'Location Select',})
TextPicker()
})
Text('Guarantee: Genuine guaranteed')
.fontSize(13)
.margin({left:10,top:5})
.alignSelf(ItemAlign.Start)
}
.height(150)
.width('100%')
}
.height(160)
.width('100%')
}
}
DetailArsList元件效果圖如下:
代碼如下:
@Component
struct DetailArsList{
private arsItems: ArsData[]
build() {
Scroll() {
Column() {
List() {
ForEach(this.arsItems, item => {
ListItem() {
ArsListItem({ arsItem: item })
}
}, item => item.id.toString())
}
.height('100%')
.width('100%')
.margin({ top: 5 })
.listDirection(Axis.Vertical)
}
.height(200)
}
}
}
ArsListItem元件代碼如下:
@Component
struct ArsListItem {
private arsItem: ArsData
build() {
Row() {
Text(this.arsItem.title+" :")
.fontSize(11)
.margin({left:20})
Blank(20)
Text( this.arsItem.content)
.fontSize(11)
.margin({left:40,right:20})
}
.height(14)
.width('100%')
.backgroundColor(Color.White)
}
}
DetailBottom元件效果圖如下:
代碼如下:
@Component
struct DetailBottom{
@Provide
private value : number=1
dialogController : CustomDialogController = new CustomDialogController({
builder: DialogExample({action: this.onAccept}),
cancel: this.existApp,
autoCancel: true
});
onAccept() {
}
existApp() {
}
build(){
Column(){
Text('Buy')
.width(40)
.height(25)
.fontSize(20)
.fontColor(Color.White)
.onClick(()=>{
this.value=1
this.dialogController.open()
})
}
.alignItems(HorizontalAlign.Center)
.backgroundColor(Color.Red)
.width('100%')
.height(40)
}
}
DialogExample自定義彈窗元件效果圖如下:
代碼如下:
@CustomDialog
@Component
struct DialogExample {
@Consume
private value : number
controller: CustomDialogController;
action: () => void;
build() {
Column() {
Progress({value: this.value++ >=100?100:this.value, total: 100, style: ProgressStyle.Capsule})
.height(50)
.width(100)
.margin({top:5})
}
.height(60)
.width(100)
}
}
想了解更多關于開源的内容,請通路: