76d3489f创建于 1月9日历史提交
/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@CustomDialog
export struct Scroll_CustomDialog {
  //控制本自定义弹窗的controller放在其他自定义弹窗的controller的最后
  controller?: CustomDialogController
  cancel: () => void = () => {
  }
  confirm: () => void = () => {
  }
  @State message: string = 'Hello World'
  @State arr: number[] = []

  @Styles
  listCard() {
    .backgroundColor(Color.White)
    .height(72)
    .width('100%')
    .borderRadius(12)
  }

  build() {
    Scroll() {
      Column({ space: 10 }) {

        Text('Scroll Area')
          .width('100%')
          .height('10%')
          .backgroundColor('#0080DC')
          .textAlign(TextAlign.Center)

        Tabs({ barPosition: BarPosition.Start }) {
          TabContent() {
            List({ space: 10 }) {
              ForEach(this.arr, (item: number) => {
                ListItem() {
                  Text('item' + item)
                    .fontSize(16)
                }.listCard()
              }, (item: string) => item)
            }.width('100%')
            .edgeEffect(EdgeEffect.Spring)
            .nestedScroll({
              scrollForward: NestedScrollMode.PARENT_FIRST,
              scrollBackward: NestedScrollMode.SELF_FIRST
            })
          }.tabBar('Tab1')

          TabContent() {
          }.tabBar('Tab2')
        }
        .vertical(false)
        .height('100%')

        Divider()
        Flex({ justifyContent: FlexAlign.SpaceAround }) {
          Button('cancel')
            .onClick(() => {
              if (this.controller != undefined) {
                this.controller.close()
                this.cancel()
              }
            }).backgroundColor(0xffffff).fontColor(Color.Black)

          Button('confirm')
            .onClick(() => {
              if (this.controller != undefined) {
                this.controller.close()
                this.confirm()
              }
            }).backgroundColor(0xffffff).fontColor(Color.Red)
        }.margin({ bottom: 10 })
      }.width('100%')
    }
    .edgeEffect(EdgeEffect.Spring)
    .friction(0.6)
    .backgroundColor('#DCDCDC')
    .scrollBar(BarState.Off)
    .width('100%')
    .height('100%')
    .borderRadius(10)
    .key('scroll_in_customDialog')
  }

  aboutToAppear() {
    for (let i = 0; i < 8; i++) {
      this.arr.push(i)
    }
  }
}

//自定义弹窗:builder打开其他弹窗类组件
@CustomDialog
export struct MainCustomDialog {
  //自定义弹窗的子弹窗的controller
  subDialogController: CustomDialogController | null = new CustomDialogController({
    builder: SubCustomDialogExample(),
    alignment: DialogAlignment.Bottom,
    offset: { dx: 0, dy: 0 }
  })
  //控制本自定义弹窗的controller放在其他自定义弹窗的controller的最后
  controller?: CustomDialogController
  //自定义弹窗的Menu
  @State menuShow: boolean = false
  @State select: boolean = true
  private iconStr: ResourceStr = $r('app.media.startIcon')
  private iconStr2: ResourceStr = $r('app.media.startIcon')

  @Builder
  SubMenu_Router() {
    Menu() {
      MenuItem({ content: '复制', labelInfo: 'Ctrl+C' })
      MenuItem({ content: '跳转', labelInfo: 'Router Go' })
    }
  }

  @Builder
  MyMenu_Router() {
    Menu() {
      MenuItem({ startIcon: $r('app.media.startIcon'), content: '跳转选项' })
      MenuItem({ startIcon: $r('app.media.startIcon'), content: '菜单选项' })
        .enabled(false)

      MenuItem({
        startIcon: this.iconStr,
        content: '菜单选项',
        endIcon: this.iconStr2,
        builder: (): void => this.SubMenu_Router()
      })

      MenuItemGroup({ header: '小标题' }) {
        MenuItem({ content: '菜单选项' })
          .selectIcon(true)
          .selected(this.select)
          .onChange((selected) => {
            console.info('menuItem select' + selected);
            this.iconStr2 = $r('app.media.startIcon');
          })
        MenuItem({
          startIcon: this.iconStr,
          content: '菜单选项',
          endIcon: this.iconStr2,
          builder: (): void => this.SubMenu_Router()
        })
      }

      MenuItem({
        startIcon: $r('app.media.startIcon'),
        content: '菜单选项',
      })
    }
  }

  //自定义弹窗的popup
  @State handlePopup: boolean = false

  build() {
    Column({ space: 10 }) {
      //自定义弹窗中弹其他弹窗类组件
      Button('点我打开弹窗类')
        .margin({ top: 10, bottom: 40 })
        .onClick(() => {
          setTimeout(() => {
            // 弹出气泡
            this.handlePopup = !this.handlePopup
          }, 100)
          setTimeout(() => {
            // 弹出子窗子弹窗
            if (this.subDialogController != null) {
              this.subDialogController.open()
            }
          }, 200)

          setTimeout(() => {
            // 弹出菜单
            this.menuShow = !this.menuShow
          }, 300)
        })

        //设置浮层
        .overlay('This is the overlay text.', {
          align: Alignment.Bottom,
          offset: { x: 0, y: 30 }
        })
        //绑定Menu
        .bindMenu(this.menuShow!!, this.MyMenu_Router(), { placement: Placement.Bottom })
        //绑定popup
        .bindPopup(this.handlePopup, {
          message: 'This is a popup with PopupOptions',
          placement: Placement.Bottom,
          showInSubWindow: false,
          primaryButton: {
            value: 'confirm',
            action: () => {
              this.handlePopup = !this.handlePopup
              console.info('confirm Button click')
            }
          },
          // 第二个按钮
          secondaryButton: {
            value: 'cancel',
            action: () => {
              this.handlePopup = !this.handlePopup
              console.info('cancel Button click')
            }
          },
          onStateChange: (e) => {
            console.info(JSON.stringify(e.isVisible))
            if (!e.isVisible) {
              this.handlePopup = false
            }
          }
        })

      Divider()
      Flex({ justifyContent: FlexAlign.SpaceAround }) {
        Button('cancel')
          .onClick(() => {
            if (this.controller != undefined) {
              this.controller.close()
            }
          }).backgroundColor(0xffffff).fontColor(Color.Black)

        Button('confirm')
          .onClick(() => {
            if (this.controller != undefined) {
              this.controller.close()
            }
          }).backgroundColor(0xffffff).fontColor(Color.Red)
      }.margin({ bottom: 10 })
    }.borderRadius(10)
  }
}

//自定义弹窗:doc示例的builder
@CustomDialog
export struct SubCustomDialogExample {
  subController?: CustomDialogController

  build() {
    Column() {
      Text('我是第二个弹窗')
        .fontSize(30)
        .height(100)
      Button('点我关闭第二个弹窗')
        .onClick(() => {
          if (this.subController != undefined) {
            this.subController.close()
          }
        })
        .margin(20)
    }
  }
}

@CustomDialog
export struct CustomDialogExample {
  @Link textValue: string
  @Link inputValue: string
  subDialogController: CustomDialogController | null = new CustomDialogController({
    builder: SubCustomDialogExample(),
    alignment: DialogAlignment.Bottom,
    offset: { dx: 0, dy: -25 }
  })
  controller?: CustomDialogController
  // 若尝试在CustomDialog中传入多个其他的Controller,以实现在CustomDialog中打开另一个或另一些CustomDialog,那么此处需要将指向自己的controller放在所有controller的后面
  cancel: () => void = () => {
  }
  confirm: () => void = () => {
  }

  build() {
    Column() {
      Text('Change text').fontSize(20).margin({ top: 10, bottom: 10 })
      TextInput({ placeholder: '', text: this.textValue }).height(60).width('90%')
        .onChange((value: string) => {
          this.textValue = value
        })
      Text('Whether to change a text?').fontSize(16).margin({ bottom: 10 })
      Flex({ justifyContent: FlexAlign.SpaceAround }) {
        Button('cancel')
          .onClick(() => {
            if (this.controller != undefined) {
              this.controller.close()
              this.cancel()
            }
          }).backgroundColor(0xffffff).fontColor(Color.Black)
        Button('confirm')
          .onClick(() => {
            if (this.controller != undefined) {
              this.inputValue = this.textValue
              this.controller.close()
              this.confirm()
            }
          }).backgroundColor(0xffffff).fontColor(Color.Red)
      }.margin({ bottom: 10 })

      Button('点我打开第二个弹窗')
        .onClick(() => {
          if (this.subDialogController != null) {
            this.subDialogController.open()
          }
        })
        .margin(20)
    }.borderRadius(10)

    // 如果需要使用border属性或cornerRadius属性,请和borderRadius属性一起使用。
  }
}

//自定义弹窗:builder设置验证customStyle
@CustomDialog
export struct UpgradeDialog1 {
  private controller: CustomDialogController

  build() {
    Column() {
      Text($r('app.string.custom_dialog_title'))
        .padding(8)
        .fontSize(24)
      this.PermissionIntroduction($r('app.media.startIcon'), $r('app.string.custom_dialog_permission_microphone'),
        $r('app.string.custom_dialog_permission_microphone_info'))
      this.PermissionIntroduction($r('app.media.startIcon'), $r('app.string.custom_dialog_permission_location'),
        $r('app.string.custom_dialog_permission_location_info'))
      Blank()
      Button($r('app.string.custom_dialog1'))
        .height(40)
        .fontSize(24)
        .fontColor(Color.White)
        .margin({ left: 4, right: 4, top: 12 })
        .onClick(() => {
          this.controller.close()
        })
    }
    .margin(20)
    .height(400)
    .borderRadius(24)
    .padding({ top: 20, bottom: 32 })
    .backgroundColor($r('app.color.background_shallow_grey'))
  }

  @Builder
  PermissionIntroduction(image: Resource, permissionName: Resource, introduction: Resource) {
    Row() {
      Image(image)
        .width(50)
        .height(50)
        .margin({ left: 30, right: 30 })
      Column({ space: 4 }) {
        Text(permissionName)
          .fontSize(20)
        Text(introduction)
          .fontSize(18)
          .fontColor($r('app.color.font_color_shallow'))
          .constraintSize({ maxWidth: 280 })
      }
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .margin({ top: 20 })
  }
}

@CustomDialog
export struct UpgradeDialog2 {
  private controller: CustomDialogController

  build() {
    Column() {
      Text($r('app.string.custom_dialog_title'))
        .padding(8)
        .fontSize(24)
      this.PermissionIntroduction($r('app.media.startIcon'), $r('app.string.custom_dialog_permission_microphone'),
        $r('app.string.custom_dialog_permission_microphone_info'))
      this.PermissionIntroduction($r('app.media.startIcon'), $r('app.string.custom_dialog_permission_location'),
        $r('app.string.custom_dialog_permission_location_info'))
      Blank()
      Button($r('app.string.custom_dialog2'))
        .height(40)
        .fontSize(24)
        .fontColor(Color.White)
        .margin({ left: 4, right: 4, top: 12 })
        .onClick(() => {
          this.controller.close()
        })
    }
    .margin(20)
    .height(400)
    .borderRadius(24)
    .padding({ top: 20, bottom: 32 })
    .backgroundColor($r('app.color.background_shallow_grey'))
  }

  @Builder
  PermissionIntroduction(image: Resource, permissionName: Resource, introduction: Resource) {
    Row() {
      Image(image)
        .width(50)
        .height(50)
        .margin({ left: 30, right: 30 })
      Column({ space: 4 }) {
        Text(permissionName)
          .fontSize(20)
        Text(introduction)
          .fontSize(18)
          .fontColor($r('app.color.font_color_shallow'))
          .constraintSize({ maxWidth: 280 })
      }
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .margin({ top: 20 })
  }
}