# 收银台设计规范

#### 功能需求

开发者可在应用项目中实现支付收银台。应用项目由配置文件（module.json5）、页面代码（ets文件）以及资源文件（图片、字符串等）组成，实现的收银台包含以下功能：展示商户信息、金额、支付方式列表（用户选择的支付方式会变成选中状态）和支付按钮。

实现效果如下（具体实现可参见示例代码）。

#### 定义收银台页面容器

定义收银台页面容器用于展示支付收银台。UX规范要求以半模态弹窗展示收银台，使用说明可参考半模态转场。

半模态容器CashierBindSheetContainer.ets示例代码如下：

```
import { CashierComponent } from './CashierComponent';

@Component
export struct CashierBindSheetContainer {
  @Builder
  cashierBuilder() {
    Column() {
      CashierComponent();
    }
  }

  build() {
    Column() {
      Column() {
      }.bindSheet(true, this.cashierBuilder(), {
        title: () => {},
        height: SheetSize.FIT_CONTENT,
        showClose: true,
        enableOutsideInteractive: false,
        backgroundColor: '#E5FFFFFF',
        blurStyle: BlurStyle.COMPONENT_THICK,
        onWillDismiss: ((action: DismissSheetAction) => {
          // 退出事件监听
        }),
      });
    }.width('100%');
  }
}
```

#### 搭建收银台页面

收银台首页CashierComponent.ets从整体上可分为三个部分：头部-商户信息展示、中部-支付方式列表、底部-支付按钮。

**示例代码如下：**

```
import { LengthMetrics } from '@kit.ArkUI';
import { getBorderRadius, isNewCardPaymentType } from '../util/PaymentUtil';
import { PaymentOrderComp } from './PaymentOrderComp';
import { PaymentItemComp } from './PaymentItemComp';
import { ConfirmButton } from './ConfirmButton';
import { PaymentType } from '../data/PaymentType';
import { paymentTypeList, paymentTypes3PayShow } from '../data/TestData';

@Preview
@Component
export struct CashierComponent {
  // 商户名称
  private mercShortName: string = "华为支付测试商户";
  // 支付金额（单位为分）
  private paymentAmount: string = '0.01';
  // 当前选中的支付方式信息
  @State selectedPaymentType: PaymentType = new PaymentType();
  @State selectedPayTypeSerialNo: string = '';

  // 头部：商户订单信息
  @Builder
  orderDetail() {
    // 后续展示实现
  }

  // 中部：支付方式列表的标题
  @Builder
  displayHeader() {
    // 后续展示实现
  }

  // 中部：其他支付方式选项
  @Builder
  moreBankFooter() {
    // 后续展示实现
  }

  // 中部：选择支付方式事件
  select(PaymentType: PaymentType) {
    // 后续展示实现
  }

  // 中部：支付方式列表
  @Builder
  paymentListBuilder() {
    // 后续展示实现
  }

  // 中部：支付方式列表 容器
  @Builder
  paymentListContent() {
    Column() {
      this.paymentListBuilder();
    }
    .margin({
      bottom: 12,
    })
  }

  // 底部：按钮区域
  @Builder
  buttonArea() {
    // 后续展示实现
  }

  // 收银台首页
  @Builder
  cashierUI() {
    // 订单信息+支付方式区域
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
      this.orderDetail();
      this.paymentListContent();
    }
    .align(Alignment.Top)
    .padding({
      left: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
      right: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
    })

    // 按钮区域
    Column() {
      this.buttonArea();
      // 底部aiBar的高度
      Row()
        .width('100%')
        .height(28)
        .margin({ top: $r('sys.float.ohos_id_elements_margin_vertical_l') })
    }
    .flexShrink(0)
    .padding({
      left: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
      right: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
    })
  }

  // 页面容器
  build() {
    Column() {
      this.cashierUI();
    }
  }
}
```

**运行结果：**

#### 展示商户信息

收银台首页CashierComponent.ets的orderDetail组件为商户信息展示。商户信息竖向居中排列，包含商户名称以及订单金额。

**示例代码如下**：

```
@Builder
orderDetail() {
  Column() {
    PaymentOrderComp({
      mercShortName: this.mercShortName,
      paymentAmount: this.paymentAmount,
    })
    .margin({ bottom: 24 });
  }
  .width('100%')
  .padding({
    top: $r('sys.float.ohos_id_elements_margin_vertical_m'),
  })
}
```

其中商户订单信息类PaymentOrderComp.ets实现如下：

```
import { Amount } from "./Amount";

@Component
export struct PaymentOrderComp {
  @Prop mercShortName: ResourceStr = '';
  @Prop paymentAmount: string = '';

  build() {
    Column() {
      Text(this.mercShortName)
        .fontFamily('HarmonyHeiTi')
        .fontSize($r('sys.float.ohos_id_text_size_body2'))
        .fontColor($r('sys.color.ohos_id_color_text_secondary'))
        .textAlign(TextAlign.Center)
        .fontWeight(FontWeight.Normal)

      Amount({
        amount: this.paymentAmount,
      }).margin({ top: 2 })
    }
  }
}
```

其中金额类Amount.ets实现如下：

```
const FONT_FAMILY_HEITI = 'HarmonyHeiTi';

@Component
@Preview
export struct Amount {
  @Prop amount: string = '';
  private currency: string = '¥';
  private color: ResourceStr = $r('sys.color.ohos_id_color_text_primary');

  build() {
    Row() {
      Text() {
        Span(this.currency)
          .fontSize(24)
          .fontFamily(FONT_FAMILY_HEITI)
          .fontWeight(FontWeight.Bold)
          .fontColor(this.color)
          .alignSelf(ItemAlign.Baseline)
        Span(this.amount)
          .fontSize(36)
          .fontFamily(FONT_FAMILY_HEITI)
          .fontWeight(FontWeight.Bold)
          .fontColor(this.color)
      }
    }.justifyContent(FlexAlign.Center)
    .alignItems(VerticalAlign.Bottom)
  }
}
```

**运行结果：**

#### 构造支付方式列表

收银台首页CashierComponent.ets的paymentListBuilder组件为收银台中部信息，包含支付方式列表。用户点击选择的支付方式会变成选中状态。

**示例代码如下**：

```
@Builder
paymentListBuilder() {
  Column() {
    List() {
      ListItemGroup({
        header: this.displayHeader(),
        style: ListItemGroupStyle.CARD,
      }) {
        ForEach(paymentTypeList, (paymentType: PaymentType) => {
          ListItem({ style: ListItemStyle.CARD }) {
            PaymentItemComp({
              paymentType: paymentType,
              canChecked: !isNewCardPaymentType(paymentType),
              isChecked: this.selectedPayTypeSerialNo === paymentType.payTypeSerialNo,
              onSelect: () => {
                if (paymentType.isAvailable) {
                  this.select(paymentType);
                }
              },
            })
          }
          .height(undefined)
          .constraintSize({ minHeight: 48 })
          .backgroundColor(Color.Transparent)
        })

        // 其他支付方式
        ListItem({ style: ListItemStyle.CARD }) {
          this.moreBankFooter();
        }
        .height(undefined)
        .constraintSize({ minHeight: 48 })
        .backgroundColor(Color.Transparent)
      }.divider({
        strokeWidth: 0.5,
        color: $r('sys.color.ohos_id_color_list_separator'),
        startMargin: 48,
        endMargin: 12,
      }).margin({
        left: 0,
        right: 0,
      }).backgroundColor($r('sys.color.comp_background_list_card'))
    }.width('100%')

    // 三方支付方式
    List() {
      ListItemGroup({ style: ListItemGroupStyle.CARD }) {
        ForEach(paymentTypes3PayShow, (paymentType: PaymentType) => {
          ListItem({ style: ListItemStyle.CARD }) {
            PaymentItemComp({
              paymentType: paymentType,
              canChecked: true,
              isChecked: this.selectedPayTypeSerialNo === paymentType.payTypeSerialNo,
              onSelect: () => {
                if (paymentType.isAvailable) {
                  this.select(paymentType);
                }
              },
            })
          }
          .height(undefined)
          .constraintSize({ minHeight: 48 })
          .backgroundColor(Color.Transparent)
        })
      }.divider({
        strokeWidth: 0.5,
        color: $r('sys.color.ohos_id_color_list_separator'),
        startMargin: 48,
        endMargin: 12,
      })
      .margin({
        left: 0,
        right: 0,
      }).backgroundColor($r('sys.color.comp_background_list_card'))
    }
    .width('100%')
    .margin({
      top: 12,
    })
  }
}
```

其中列表标题组件displayHeader示例代码如下：

```
@Builder
displayHeader() {
  Column() {
    Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
      Image($r('app.media.kit_hwpay_wallet'))
        .borderRadius(getBorderRadius(24))
        .width(24)
        .height(24)
        .margin({ end: LengthMetrics.vp(16) })

      Text($r('app.string.kit_hwpay_desc'))
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .fontFamily('HarmonyHeiTi-Medium')
        .fontColor($r('sys.color.ohos_id_color_text_primary'))
    }
    .width('100%')
    .constraintSize({ minHeight: 48 })
    .padding({
      left: 8,
      right: 8,
      top: 12,
      bottom: 12,
    })

    Divider()
      .color($r('sys.color.ohos_id_color_list_separator'))
      .width('100%')
      .margin({
        start: LengthMetrics.vp(48),
        end: LengthMetrics.vp(12),
      })
  }
}
```

其中其它支付方式组件moreBankFooter示例代码如下：

```
@Builder
moreBankFooter() {
  Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
    Text($r('app.string.kit_hwpay_change_other_pay_type'))
      .fontSize(16)
      .fontWeight(FontWeight.Medium)
      .fontFamily('HarmonyHeiTi-Medium')
      .fontColor($r('sys.color.ohos_id_color_text_primary'))

    Image($r('app.media.kit_hwpay_right_v2'))
      .matchTextDirection(true)
      .width(12)
      .height(24)
      .fillColor($r('sys.color.ohos_id_color_fourth'))
  }
  .padding({
    top: 12,
    bottom: 12,
  })
  .margin({ start: LengthMetrics.vp(40) })
}
```

其中支付方式组件类PaymentItemComp.ets实现展示可用、不可用的支付方式，添加银行卡以及用户选中支付方式后展示勾选效果。示例代码如下：

```
import { LengthMetrics } from '@kit.ArkUI';
import { PaymentType } from '../data/PaymentType';
import { getBorderRadius } from '../util/PaymentUtil';

export enum ButtonOpacity {
  // 默认不透明度
  PRIMARY = 1,
  // 按钮置灰不透明度
  DISABLED = 0.6,
  // 支付方式不可用导致的按钮不可用的不透明度
  PAYMENT_UNAVAILABLE = 0.38
}

@Component
export struct PaymentItemComp {
  @Prop paymentType: PaymentType;
  @Prop canChecked: boolean = false;
  @Prop isChecked?: boolean = false;
  onSelect: () => void = () => {
  };

  build() {
    Row() {
      Image($r("app.media.payment_logo"))
        .borderRadius(getBorderRadius(24))
        .width(24)
        .height(24)
        .margin({ end: LengthMetrics.vp(16) })

      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center }) {
        Flex({
          direction: FlexDirection.Row,
          justifyContent: FlexAlign.Start,
          alignItems: ItemAlign.Center,
        }) {
          Text(this.paymentType?.payTypeDesc)
            .fontSize(16)
            .fontWeight(FontWeight.Medium)
            .fontFamily('HarmonyHeiTi-Medium')
            .fontColor($r('sys.color.ohos_id_color_text_primary'))
        }
        .padding({
          top: (this.paymentType?.paymentTypeTip) ? 4 : 0,
        })

        if (this.paymentType?.paymentTypeTip) {
          Row() {
            Text(this.paymentType?.paymentTypeTip)
              .fontSize($r('sys.float.ohos_id_text_size_body3'))
              .fontColor($r('sys.color.comp_focused_secondary'))
              .fontFamily('HarmonyHeiTi')
              .fontWeight(FontWeight.Normal)
              .width('90%')
              .padding({ top: 4, bottom: 4 })
          }
        }
      }
      .layoutWeight(1)

      if (this.canChecked) {
        Radio({ value: this.paymentType?.payTypeSerialNo as string, group: 'aggrPaymentRadioGroup' })
          .visibility(this.isChecked ? Visibility.Visible : Visibility.Hidden)
          .checked(this.isChecked)
          .height(20)
          .width(20)
          .margin(2)
      } else {
        Image($r('app.media.kit_hwpay_right_v2'))
          .width(12)
          .height(24)
          .fillColor($r('sys.color.ohos_id_color_fourth'))
          .visibility(this.paymentType?.isAvailable ? Visibility.Visible : Visibility.None)
      }
    }
    .padding({
      top: 12,
      bottom: 12,
    })
    .opacity(this.paymentType?.isAvailable ? ButtonOpacity.PRIMARY : ButtonOpacity.PAYMENT_UNAVAILABLE)
    .alignItems(VerticalAlign.Center)
    .onClick(() => {
      if (!this.paymentType?.isAvailable) {
        return;
      }
      this.onSelect();
    })
  }
}
```

收银台首页CashierComponent.ets中选择支付方式事件示例代码如下：

```
select(PaymentType: PaymentType) {
  this.selectedPaymentType = PaymentType;
  this.selectedPayTypeSerialNo = PaymentType.payTypeSerialNo || '';
}
```

**运行结果：**

#### 添加支付按钮

收银台首页CashierComponent.ets的buttonArea组件为收银台底部信息，包含支付按钮。开发者可通过按钮点击事件实现具体支付处理逻辑。

**示例代码如下**：

```
@Builder
buttonArea() {
  ConfirmButton({
    text: $r('app.string.kit_hwpay_checkout_confirm_pay'),
  })
  .margin({
    top: $r('sys.float.ohos_id_elements_margin_vertical_l'),
  });
}
```

其中确认支付按钮类ConfirmButton.ets示例代码如下：

```
@Component
@Preview
export struct ConfirmButton {
  @Prop text: ResourceStr = '';

  build() {
    Column() {
      Row() {
        Button(this.text) {
          Row() {
            Text(this.text)
              .fontSize($r('sys.float.Body_L'))
              .fontWeight(FontWeight.Medium)
              .fontFamily('HarmonyHeiTi-Medium')
              .fontColor($r('sys.color.ohos_fa_text_contrary'))
              .textAlign(TextAlign.Center)
              .textOverflow({
                overflow: TextOverflow.Ellipsis
              })
              .maxLines(1)
          }
          .alignItems(VerticalAlign.Center)
          .justifyContent(FlexAlign.Center)
        }
        .focusOnTouch(true)
        .type(ButtonType.Normal)
        .borderRadius(20)
        .backgroundColor($r('sys.color.ohos_id_color_floating_button_bg_normal'))
        .constraintSize({
          minWidth: '100%',
          maxWidth: '100%',
          minHeight: 40,
          maxHeight: 40,
        })
        .padding({
          top: 4,
          bottom: 4,
          left: 16,
          right: 16,
        })
        .opacity(1)
        .stateEffect(true)
      }
      .onClick(() => {
        // 按钮点击事件
      })
    }.width('100%')
    .alignItems(HorizontalAlign.Center)
  }
}
```

**运行结果：**

#### 页面数据定义

#### 字符串、图片等资源

文件摆放位置为“src/main/resources”相应的目录下。

#### 支付方式及支付类型枚举

支付方式PaymentType.ets类及支付类型枚举CustPayType.ets类定义示例代码如下：

```
// 支付方式：PaymentType.ets
export class PaymentType {
  payTypeSerialNo?: string;
  payTypeDesc?: string;
  paymentTypeTip?: string;
  isAvailable?: boolean;
  custPayType: string = '';
}

// 支付类型枚举：CustPayType.ets
export enum CustPayType {
  NewBankCard = 'NewBankCard',
}
```

#### 支付方式测试数据

测试使用的支付方式数据TestData.ets类定义示例代码如下：

```
import { PaymentType } from "./PaymentType";

export const paymentTypeList: PaymentType[] = [
  {
    payTypeSerialNo: '1',
    payTypeDesc: 'xx银行储蓄卡 (0606)',
    isAvailable: true,
    custPayType: 'BankFastPayment',
  },
  {
    payTypeSerialNo: '2',
    payTypeDesc: 'xx银行储蓄卡 (0909)',
    isAvailable: true,
    custPayType: 'BankFastPayment',
  }
];

export const paymentTypes3PayShow: PaymentType[] = [
  {
    payTypeSerialNo: '3',
    payTypeDesc: '云闪付',
    isAvailable: true,
    custPayType: 'CloudFlashPayment',
  },
  {
    payTypeSerialNo: '4',
    payTypeDesc: '微信支付',
    isAvailable: true,
    custPayType: 'WECHAT_MICROPAY',
  }
]
```

#### 工具类

支付工具PaymentUtil.ets类定义示例代码如下：

```
import { CustPayType } from "../data/CustPayType";
import { PaymentType } from "../data/PaymentType";

/**
 * 支付工具是否是新卡
 * @param paymentType 支付方式
 * @returns 是否为新卡支付方式
 */
export const isNewCardPaymentType = (paymentType: PaymentType): boolean => {
  if (!paymentType) {
    return false;
  }
  return paymentType.custPayType === CustPayType.NewBankCard;
};

/**
 * 获取图标圆角值
 * @param length 圆角值
 * @returns 图标圆角值
 */
export const getBorderRadius = (length: number): number => {
  // 正方形图标圆角值为：14 / 54 * 图标边长
  return 7 / 27 * length;
}
```