鸿蒙(HarmonyOS)常见的三种弹窗方式

最近有一个想法,做一个针对鸿蒙官方API的工具箱项目,介绍常用的控件,以及在项目中如何使用,今天介绍Harmony中如何实现弹窗功能。

警告弹窗

警告弹窗是一个App中非常常用的弹窗,例如:

  • 删除一条记录,提示一下用户:您确定要删除吗?
  • 在App首页,点击返回时,提示一下用户:您确定要退出App吗?

使用AlertDialog.show方法进行弹窗,这个方法支持传入以下三个类中的任意一个对象

  • AlertDialogParamWithConfirm
  • AlertDialogParamWithButtons
  • AlertDialogParamWithOptions

以AlertDialogParamWithButtons对象进行说明,下面表格介绍常用属性:

参数名参数类型必填参数描述
titleResourceStr弹窗标题
messageResourceStr弹窗内容
autoCancelboolean点击遮障层时,是否关闭弹窗。默认值:true
primaryButton{value: ResourceStr,fontColor?: ResourceColor,backgroundColor?: ResourceColor,action: () => void;}按钮的文本内容、文本色、按钮背景色和点击回调
secondaryButton{value: ResourceStr,fontColor?: ResourceColor,backgroundColor?: ResourceColor,action: () => void;}按钮的文本内容、文本色、按钮背景色和点击回调
cancel() => void点击遮障层关闭dialog时的回调
alignmentDialogAlignment弹窗在竖直方向上的对齐方式。默认值:DialogAlignment.Default

接下来,我们用代码来实现一下:

AlertDialog.show({
    title:"弹窗标题",
    message:"这是弹窗内容",
    autoCancel:true,//点击遮障层时,是否关闭弹窗。默认值:true
    alignment: DialogAlignment.Center,//弹窗在竖直方向上的对齐方式。默认值:DialogAlignment.Default
    primaryButton: {
        value: "取消",
        fontColor: '#181818',
        action: () => {
            AppUtil.showToast("点击了取消按钮");
        }
    },
    secondaryButton: {
        value: "确定",
        fontColor: $r('app.color.mainColor'),
        action: () => {
            AppUtil.showToast("点击了确定按钮");
        }
    },
    cornerRadius:12,//弹窗边框弧度
    width:'80%', //弹窗宽度
    cancel:()=>{
        AppUtil.showToast("点击遮障层关闭dialog时的回调");
    }
})

效果图:

参考官方链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-methods-alert-dialog-box-0000001478341185-V2

自定义弹窗

自定义弹窗相比警告弹窗更为灵活,支持自定义弹窗的样式与内容。

自定义弹窗的参数:
参数名参数类型必填参数描述
builderCustomDialog自定义弹窗内容构造器。
cancel() => void点击遮障层退出时的回调。
autoCancelboolean是否允许点击遮障层退出。默认值:true
alignmentDialogAlignment弹窗在竖直方向上的对齐方式。默认值:DialogAlignment.Default
offsetOffset弹窗相对alignment所在位置的偏移量。
customStyleboolean弹窗容器样式是否自定义。默认值:false,弹窗容器的宽度根据栅格系统自适应,不跟随子节点;高度自适应子节点,最大为窗口高度的90%;圆角为24vp。
gridCount8+number弹窗宽度占栅格宽度的个数。默认为按照窗口大小自适应,异常值按默认值处理,最大栅格数为系统最大栅格数。

代码实现

我们使用自定义弹窗实现隐私政策弹窗,新建PrivacyPolicyDialogBackUp类,也就是内容构造器,使用@CustomDialog修饰,内部有一个属性controller: CustomDialogController,这些都是常规写法,然后在build中实现界面布局。

@CustomDialog
export default struct PrivacyPolicyDialogBackUp{
    controller: CustomDialogController

    cancel!: () => void
    confirm!: () => void

    build() {
        Column() {
            Text($r('app.string.simple_user_policy')).fontSize(18).fontColor($r('app.color.title_color')).margin({ top: 30, bottom: 19 })

            Scroll(){
                Text(){
                    Span($r('app.string.privacy_policy_start'))
                    Span($r('app.string.user_agreement_two')).fontColor($r('app.color.mainColor')).onClick(() => {
                        this.openWebUrl("/useragreement.html");
                    })
                    Span($r('app.string.and'))
                    Span($r('app.string.privacy_policy_two')).fontColor($r('app.color.mainColor')).onClick(() => {
                        this.openWebUrl("/privacypolicy.html");
                    })
                    Span($r('app.string.simple_privacy_policy'))
                }.fontSize(16).fontColor($r('app.color.body_color')).margin({
                    left:25,
                    right:25
                })
            }.height(120)

            Column(){
                Button($r('app.string.disagree_privacy_policy')).onClick(() => {
                    this.controller.close();
                    this.cancel();
                }).fontColor($r('app.color.other_color')).fontSize(15).backgroundColor(Color.Transparent)

                Button($r('app.string.agree_privacy_policy')).onClick(() => {
                    this.controller.close();
                    this.confirm();
                }).fontColor($r('app.color.white')).fontSize(17)
                    .linearGradient({
                        direction: GradientDirection.Right, colors:[[$r('app.color.start_main_color'),0.0],[$r('app.color.end_main_color'),1.0]]
                    }).width('80%').margin({
                    top:15,bottom:21
                }).borderRadius(24)
            }
        }
    }

    openWebUrl(urlSuffix:string){
        let url= AppConstant.URL+urlSuffix;
        logger.info("url:"+url)
        router.pushUrl({
            url: Pages.WebViewPage,
            params:{
                data1: 'message',
                url: url,  // 传递的 URL 参数
            }
        }, router.RouterMode.Single)
    }
}

在组件中创建CustomDialogController实例,指定builder属性,就是上面写的内容构造器

privacyPolicyDialog: CustomDialogController = new CustomDialogController({
  builder: PrivacyPolicyDialog({
    cancel:this.onCancel.bind(this),
    confirm:this.onAgree.bind(this)
  }),
  alignment: DialogAlignment.Default,  // 可设置dialog的对齐方式,设定显示在底部或中间等,默认为底部显示
  cornerRadius:13,
  autoCancel:false
})

显示弹窗

this.privacyPolicyDialog.open();

关闭弹窗

this.privacyPolicyDialog.close();

效果图:

参考官方链接:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-methods-custom-dialog-box-0000001477981237-V2

加载中弹窗

加载中弹窗弹窗其实就是自定义弹窗实现,只是内容构造器不一样而已,给Image组件设置animation动画,无限循环图片

@CustomDialog
export default struct LoadingDialog {
    controller: CustomDialogController

    private loadingText: string|Resource = "加载中..."
    @State angle:number = 10

    aboutToAppear(){
        setTimeout(()=>{
            this.angle = 1440 // 设定一个大的旋转角度,确保动画执行
        },100)
    }

    build() {
        Column(){
            Image($r('app.media.icon_loading_3'))
                .width(70)
                .height(70)
                .rotate({angle:this.angle})
                .animation({
                    duration: 5000,
                    curve: Curve.Linear,
                    delay: 0,
                    iterations: -1, // 设置-1表示动画无限循环
                    playMode: PlayMode.Normal
                })


            Text(this.loadingText).fontSize(14).fontColor(0xffffff).margin({top:10})
        }.backgroundColor(0x88000000).borderRadius(10).padding({
            left:20,right:20,top:10,bottom:10
        })
    }
}

效果图:

源码下载:

https://github.com/ansen666/harmony_tools

如果您想第一时间看我的后期文章,扫码关注公众号

      安辉编程笔记 - 开发技术分享
             扫描二维码加关注

安辉编程笔记

<think>好的,我现在需要帮助用户解决在vxe-table中实现点击按钮弹出对话框的问题。用户之前已经询问过Vue3中如何实现点击按钮弹出弹窗,现在他们想在vxe-table的上下文中实现类似的功能,并参考了引用中的示例代码,特别是引用[1]中的el-switch和el-popconfirm的组合使用,以及引用[2]中提到的vxe-table的事件处理。 首先,我需要确认用户的需求:他们希望在表格的某一列中添加按钮,点击该按钮时弹出对话框。这涉及vxe-table的列配置、按钮事件绑定以及对话框组件的控制。 接下来,回顾用户提供的引用内容。引用[1]中的示例使用了element-ui的el-popconfirm组件,在el-switch的change事件中触发确认弹窗。用户可能需要类似的事件处理机制,但改用vxe-table的自定义列和按钮。引用[2]提到了vxe-table的事件,如cell-click和radio-change,这些事件可能用于触发对话框的显示。另外,引用[3]提到Jeecgboot-Vue3的前端技术栈,但可能暂时不相关,除非用户正在使用该框架,但用户的问题中没有提到,所以暂时忽略。 接下来,我需要构建一个示例,展示如何在vxe-table中添加按钮列,并在点击按钮时弹出对话框。根据引用[2]中vxe-table的文档,可以使用slots来自定义列内容,插入按钮。然后,通过按钮的点击事件触发对话框的显示状态变更。 需要注意以下几点: 1. 使用vxe-table的columns配置,定义按钮列,使用slots插入按钮元素。 2. 绑定按钮的click事件到方法,该方法设置显示对话框的状态变量。 3. 使用Vue3的响应式数据(如ref)控制对话框的显示与隐藏。 4. 可能需要传递当前行的数据到对话框,以便显示相关信息或进行操作。 此外,用户之前的回答中提到了使用teleport将模态框渲染到body末端,这可能也是需要考虑的优化点,避免模态框被表格的父容器样式影响。 然后,我需要确保代码示例正确无误,包括正确的vxe-table语法和Vue3的组合式API。例如,在setup语法中使用ref定义showDialog和selectedRow,以及在columns配置中使用{ field: 'action', title: '操作', slots: { default: 'action' } },并在模板中使用v-slot:action来定义按钮。 可能还需要处理对话框中的确认和取消操作,更新数据或调用API。例如,在用户点击对话框的确认按钮后,执行某些操作,比如更新表格数据或向后端发送请求。 最后,检查是否有遗漏的步骤,如样式处理、事件解绑等,确保示例的完整性和正确性。同时,考虑用户可能需要的进一步优化,比如使用teleport、过渡动画,或者将对话框封装为可复用组件,这些在之前的回答中已有涉及,可以在相关问题中提及。</think>### 基于`vxe-table`实现点击按钮弹出对话框的完整方案 #### 核心实现步骤 1. **表格列按钮定义** 通过`slots`自定义操作列并绑定点击事件: ```vue <script setup> import { ref } from 'vue' import { VXETable } from 'vxe-table' const tableData = ref([ { id: 1, name: '用户1' }, { id: 2, name: '用户2' } ]) // 对话框控制 const showDialog = ref(false) const selectedRow = ref(null) // 列配置 const columns = [ { field: 'id', title: 'ID' }, { field: 'name', title: '姓名' }, { field: 'action', title: '操作', slots: { default: 'action' } // 启用插槽 } ] // 按钮点击处理 const handleClick = (row) => { selectedRow.value = row showDialog.value = true } </script> ``` 2. **表格模板结构** 使用`v-slot`定义操作列内容: ```vue <template> <vxe-table :data="tableData" :columns="columns"> <!-- 操作列模板 --> <template #action="{ row }"> <vxe-button @click="handleClick(row)">查看详情</vxe-button> </template> </vxe-table> </template> ``` 3. **对话框组件集成** 使用`teleport`实现独立渲染: ```vue <teleport to="body"> <div v-if="showDialog" class="dialog-mask"> <div class="dialog-container"> <h3>当前选中行数据</h3> <pre>{{ selectedRow }}</pre> <vxe-button @click="showDialog = false">关闭</vxe-button> </div> </div> </teleport> ``` #### 完整组件示例 ```vue <script setup> import { ref } from 'vue' import { VXETable } from 'vxe-table' // 表格数据 const tableData = ref([ { id: 1, name: '用户1', age: 25 }, { id: 2, name: '用户2', age: 30 } ]) // 对话框状态 const showDialog = ref(false) const selectedRow = ref(null) // 列配置 const columns = [ { field: 'id', title: 'ID', width: 100 }, { field: 'name', title: '姓名', minWidth: 120 }, { field: 'action', title: '操作', width: 150, slots: { default: 'action' } } ] // 按钮点击处理 const handleAction = (row) => { selectedRow.value = row showDialog.value = true } </script> <template> <vxe-table border :data="tableData" :columns="columns" > <template #action="{ row }"> <vxe-button status="primary" @click="handleAction(row)" >显示详情</vxe-button> </template> </vxe-table> <!-- 对话框 --> <teleport to="body"> <div v-if="showDialog" class="dialog-mask"> <div class="dialog-content"> <h3>用户详细信息(ID: {{ selectedRow?.id }})</h3> <vxe-table border :data="[selectedRow]" :columns="[ { field: 'name', title: '姓名' }, { field: 'age', title: '年龄' } ]" /> <div class="dialog-footer"> <vxe-button @click="showDialog = false">关闭</vxe-button> </div> </div> </div> </teleport> </template> <style scoped> .dialog-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; } .dialog-content { background: white; padding: 20px; border-radius: 8px; min-width: 400px; } .dialog-footer { margin-top: 15px; text-align: right; } </style> ``` #### 关键实现原理 1. **事件穿透处理** 使用`vxe-button`代替原生按钮,避免表格事件冲突[^2] 2. **数据绑定机制** 通过作用域插槽获取当前行数据:`#action="{ row }"` 3. **状态隔离** 使用`teleport`将对话框渲染到body末端,避免z-index层级问题 #### 进阶优化方向 1. **复用对话框组件** 创建独立`DetailDialog.vue`组件: ```vue <!-- DetailDialog.vue --> <script setup> defineProps({ visible: Boolean, rowData: Object }) const emit = defineEmits(['close']) </script> <template> <teleport to="body"> <div v-if="visible" class="dialog-mask"> <div class="dialog-content"> <!-- 内容模板 --> <slot :row="rowData"></slot> <vxe-button @click="emit('close')">关闭</vxe-button> </div> </div> </teleport> </template> ``` 2. **表格行样式增强** 结合`cell-class-name`实现行高亮: ```js const columns = [ //... { field: 'action', cellClassName: ({ row }) => row.id === selectedRow.value?.id ? 'highlight-row' : '' } ] ``` 3. **批量操作支持** 通过复选框实现多选操作: ```vue <vxe-table :checkbox-config="{ highlight: true }" @checkbox-change="handleBatchSelect"> <!-- 列定义 --> </vxe-table> ``` --相关问题--: 1. 如何在vxe-table中实现双击行弹出对话框? 2. vxe-table如何实现动态列渲染? 3. 如何优化vxe-table大数据量的渲染性能?[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安辉就是我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值