Technical Stack
Appgallery connect
Development Preparation
In the previous section, we implemented coupon query on the order confirmation page, but lacked other coupon-related logic. Our goal is to use corresponding coupons during order settlement. Now, we need to enable coupon selection on the order confirmation page, presenting it in a pop-up window for user convenience.
Functional Analysis
To display coupons in a pop-up window:
Create a custom pop-up dialog.
Query all coupon data to show both usable and unusable coupons.
Inform users of usable coupon amounts and the remaining amount needed for unusable coupons to be valid.
Allow clicking on usable coupons to modify the settlement amount and display the selected coupon amount.
Code Implementation
First, create the pop-up window with two-way binding for coupon amount:
@Preview
@CustomDialog
export struct CouponCheckDialog {
@State user: User | null = null
@State couponList: CouponMall[] = []
controller: CustomDialogController;
@State lessThanLimit: CouponMall[] = []
@State greaterOrEqualLimit: CouponMall[] = []
@prop price: number
@link couponPrice: number
async aboutToAppear(): Promise {
const value = await StorageUtils.getAll('user');
if (value != "") {
this.user = JSON.parse(value)
}
let databaseZone = cloudDatabase.zone('default');
let condition = new cloudDatabase.DatabaseQuery(coupon_mall);
condition.equalTo("user_id", this.user?.user_id)
let listData = await databaseZone.query(condition);
let json = JSON.stringify(listData)
let data: CouponMall[] = JSON.parse(json)
this.couponList = data
this.splitCouponsByLimit(data, this.price)
}
@builder
Header(type: number) {
Text(type == 0 ? "Usable Coupons" : "Unusable Coupons")
.width('100%')
.padding(10)
.textAlign(TextAlign.Start)
.fontColor(Color.Black)
.fontSize(16)
}
@builder
Item(item: CouponMall) {
Column({ space: 10 }) {
Row({ space: 10 }) {
Column() {
Text("¥" + item.price)
.fontSize(30)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
Text("Valid for orders over ¥" + item.limit_amount)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
.fontSize(12)
}
Column({ space: 10 }) {
Text(item.type_str)
.fontColor(Color.Black)
.fontWeight(FontWeight.Bold)
.fontSize(16)
Text(item.txt)
.fontColor(Color.Grey)
.fontSize(12)
}
Blank()
Text("Ready to Use")
.width(80)
.height(80)
.borderRadius(40)
.fontSize(14)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.backgroundColor(Color.Red)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Divider().width('100%').height(0.8)
.color("#e6e6e6")
Text("Valid from " + item.start_time + " to " + item.end_time)
.fontSize(12)
.fontColor(Color.Grey)
}
.margin({ top: 10 })
.padding(10)
.backgroundColor(Color.White)
.borderRadius(10)
.onClick(() => {
this.couponPrice = item.price
this.controller.close()
})
}
@builder
NotItem(item: CouponMall) {
Column({ space: 10 }) {
Row({ space: 10 }) {
Column() {
Text("¥" + item.price)
.fontSize(30)
.fontColor(Color.Grey)
.fontWeight(FontWeight.Bold)
Text("Valid for orders over ¥" + item.limit_amount)
.fontColor(Color.Grey)
.fontWeight(FontWeight.Bold)
.fontSize(12)
}
Column({ space: 10 }) {
Text(item.type_str)
.fontColor(Color.Grey)
.fontWeight(FontWeight.Bold)
.fontSize(16)
Text(item.txt)
.fontColor(Color.Grey)
.fontSize(12)
}
Blank()
Text("Unusable")
.width(80)
.height(80)
.borderRadius(40)
.fontSize(14)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.backgroundColor(Color.Grey)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Divider().width('100%').height(0.8)
.color("#e6e6e6")
Text("Valid from " + item.start_time + " to " + item.end_time)
.fontSize(12)
.fontColor(Color.Grey)
Text() {
Span("Reason for unavailability: ")
.fontColor(Color.Red)
.fontSize(12)
Span("Order amount needs ¥")
.fontColor(Color.Gray)
.fontSize(12)
Span("" + (item.limit_amount - this.price))
.fontColor(Color.Red)
.fontSize(12)
Span(" more to meet the threshold")
.fontColor(Color.Gray)
.fontSize(12)
}
.backgroundColor("#fffacfcf")
.padding(5)
.borderRadius(5)
}
.margin({ top: 10 })
.padding(10)
.backgroundColor(Color.White)
.borderRadius(10)
.onClick(() => {
showToast("Coupon is not usable")
})
}
build() {
Column() {
Text("Select Coupon")
.fontSize(18)
.fontColor(Color.Black)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
.padding(10)
.backgroundColor(Color.White)
List({ space: 10 }) {
ListItemGroup({ header: this.Header(0) }) {
ForEach(this.lessThanLimit, (item: CouponMall, index: number) => {
ListItem() {
this.Item(item)
}
})
}
ListItemGroup({ header: this.Header(1) }) {
ForEach(this.greaterOrEqualLimit, (item: CouponMall, index: number) => {
ListItem() {
this.NotItem(item)
}
})
}
}
.padding(10)
Button("Confirm Selection", { type: ButtonType.Normal })
.width('90%')
.height(50)
.backgroundColor(Color.Red)
.fontColor(Color.White)
.borderRadius(25)
.margin({ top: 20, bottom: 30 })
.onClick(() => {
this.controller.close()
})
}
.width('100%')
.backgroundColor(Color.White)
}
splitCouponsByLimit(coupons: CouponMall[], price: number) {
const lessThanLimit = coupons.filter(coupon =>
coupon.limit_amount <= price
);
this.lessThanLimit = lessThanLimit;
const greaterOrEqualLimit = coupons.filter(coupon =>
coupon.limit_amount > price
);
this.greaterOrEqualLimit = greaterOrEqualLimit;
}
}
Reference the pop-up in the order confirmation page:
couponController: CustomDialogController | null = new CustomDialogController({
builder: CouponCheckDialog({
couponPrice: this.couponPrice,
price: this.price()
}),
alignment: DialogAlignment.Bottom,
customStyle: true
});
Modify the coupon display component logic:
Row() {
Text("Coupons")
.fontSize(14)
.fontColor(Color.Black)
if (this.getCoupon() > 0) {
if (this.couponPrice > 0) {
Text("Selected: -¥" + this.couponPrice + " >")
.fontSize(14)
.fontColor(Color.Red)
.onClick(() => {
this.couponController?.open()
})
} else {
Text(this.getCoupon() + " available >")
.fontSize(14)
.fontColor(Color.Red)
.onClick(() => {
this.couponController?.open()
})
}
} else {
Text("No available coupons")
.fontSize(14)
.fontColor(Color.Black)
}
}
.padding(10)
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Top comments (0)