HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现

HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现

最近在学习鸿蒙开发过程中,阅读了官方文档,在之前做flutter时候,经常使用overlay,使用OverlayEntry加入到overlayState来做添加悬浮按钮、提示弹窗、加载中指示器、加载失败的toast等功能。那在HarmonyOS鸿蒙开发中也可能有类似的功能需求。

HarmonyOS鸿蒙开发的使用弹窗文档中已经非常详细了
地址:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-use-dialog-V5

一、子窗口window

在弹出的loading指示器中,我们可以使用创建子window的方式,调用window的loadContentByName方法来实现。
实现步骤

效果预览

在这里插入图片描述

  • 1、实现加载中的loading组件,这里定义名字为LoadingHud

在LoadingHud中有LoadingProgress、Text提示文本,Text显示的信息由LocalStorage进行传递
需要传递的数据message,在aboutToAppear进行赋值

@Local message: string = '';

  aboutToAppear(): void {
    this.message = LocalStorage.getShared().get("message") ?? "";
  }

当然在调用window的loadContentByName时候,需要确定加载的主角的routeName,这就需要在LoadingHud组件中使用装饰器来设置

/// 通用的hud,弹出框,或者loading框
@Entry({ routeName: "hudLoading", storage: LocalStorage.getShared() })
@ComponentV2
export struct LoadingHud {
  ... 其他代码
}

LoadingHud的完整代码如下:

/// 通用的hud,弹出框,或者loading框
@Entry({ routeName: "hudLoading", storage: LocalStorage.getShared() })
@ComponentV2
export struct LoadingHud {
  @Local message: string = '';

  aboutToAppear(): void {
    this.message = LocalStorage.getShared().get("message") ?? "";
  }

  build() {
    Column() {
      Column(){
        Row() {
          // 从左往右,1号环形进度条,默认前景色为蓝色渐变,默认strokeWidth进度条宽度为2.0vp
          LoadingProgress()
            .color($r('app.color.success'))
            .width(40)
            .height(40)

          // message
          Text(this.message)
            .fontSize(14)
            .fontColor($r('app.color.dataset_empty_message'))
            .margin({
              left: 10
            })
        }
        .padding({
          top: 15,
          bottom: 15,
          left: 15,
          right: 20
        })
        .justifyContent(FlexAlign.Center)

        Button("点击消失")
          .width(100)
          .height(40)
          .fontSize(12)
          .backgroundColor('#ef04792c')
          .margin({
            top: 10
          })
          .onClick(()=> {
             LoadingHudUtil.dismissLoading();
          })
      }
      .justifyContent(FlexAlign.Center)
      .constraintSize({
        minWidth: 200,
        minHeight: 150,
      })
      .backgroundColor($r('app.color.white'))
      .borderRadius(10)
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
    .backgroundColor('#00000000')
    .hitTestBehavior(HitTestMode.Transparent)
  }
}

  • 2、创建子Window并显示

在创建LoadingHud后,我们需要创建创建子Window并显示window,显示我们的loadingHUD
创建window的createWindow,这里使用的windowType是window.WindowType.TYPE_DIALOG,也可以换成其他的试试看。

let windowName = "loading";
      // 创建窗口
      let subWindow = await window.createWindow(
        {
          name: windowName,
          windowType: window.WindowType.TYPE_DIALOG,
          ctx: ctx,
        }
      );

设置LocalStorage数据,存储message

//创建存储
      let storage = new LocalStorage();
      //存储数据
      storage.setOrCreate('message',  tip);

调用window的loadContentByName,设置Window的大小及背景颜色,显示Window

await subWindow.loadContentByName('hudLoading', storage);
      let dp = display.getDefaultDisplaySync();
      await subWindow.resize(dp.width, dp.height);
      subWindow.setWindowBackgroundColor('#30000000');
      await subWindow.showWindow();

显示后Window,在需要消失的时候调用destroyWindow

static async dismissLoading(): Promise<void> {
    if (LoadingHudUtil.cacheWindow) {
      await LoadingHudUtil.cacheWindow.destroyWindow();
    }
  }

完整的LoadingHudUtil的代码如下

import { display, window } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';
import('../components/hud/LoadingHud'); // 引入命名路由页面

// 自定义弹出窗口
export class LoadingHudUtil {
  private static cacheWindow: window.Window;

  static async showLoading(tip: string): Promise<void> {
    let ctx = getContext() as common.UIAbilityContext;
    try {
      let windowName = "loading";
      // 创建窗口
      let subWindow = await window.createWindow(
        {
          name: windowName,
          windowType: window.WindowType.TYPE_DIALOG,
          ctx: ctx,
        }
      );

      LoadingHudUtil.cacheWindow = subWindow;

      //创建存储
      let storage = new LocalStorage();
      //存储数据
      storage.setOrCreate('message',  tip);

      console.log("LoadingHudUtil loadContentByName" + tip);

      // subWindow.setGestureBackEnabled(false);
      // subWindow.setDialogBackGestureEnabled(false);
      // subWindow.setWindowTouchable(true);
      await subWindow.loadContentByName('hudLoading', storage);
      let dp = display.getDefaultDisplaySync();
      await subWindow.resize(dp.width, dp.height);
      subWindow.setWindowBackgroundColor('#30000000');
      await subWindow.showWindow();
    } catch (e) {
      console.log("LoadingHudUtil showLoading e:" + JSON.stringify(e));
    }
  }

  static async dismissLoading(): Promise<void> {
    if (LoadingHudUtil.cacheWindow) {
      await LoadingHudUtil.cacheWindow.destroyWindow();
    }
  }
}

二、自定义Dialog

在HarmonyOS鸿蒙开发中,可以使用CustomDialogController来实现自定义的弹窗。

效果预览
在这里插入图片描述

  • 1.自定义弹窗组件CustomAlertDialog

在CustomAlertDialog中实现一个消息提示,并且点击按钮可以关闭dialog
代码如下:

@CustomDialog
export struct CustomAlertDialog {
  controller?: CustomDialogController
  title?: string

  build() {
    Column() {
      Column() {
        // message
        Text(this.title)
          .fontSize(14)
          .fontColor($r('app.color.dataset_empty_message'))
          .margin({
            left: 10
          })

        Button("点击消失")
          .width(100)
          .height(40)
          .fontSize(12)
          .backgroundColor('#ef04792c')
          .margin({
            top: 10
          })
          .onClick(() => {
            this.controller?.close();
          })
      }
      .justifyContent(FlexAlign.Center)
      .constraintSize({
        minWidth: 200,
        minHeight: 100,
      })
      .backgroundColor($r('app.color.white'))
      .borderRadius(10)
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Transparent)
    .hitTestBehavior(HitTestMode.Transparent)
  }
}
  • 2.使用CustomDialogController来展示弹窗

定义CustomDialogController

// 自定义CustomDialog
  customDialogController: CustomDialogController | null = new CustomDialogController({
    builder: CustomAlertDialog({
      title: "温馨提示"
    }),
    alignment: DialogAlignment.Center,
    onWillDismiss: (dismissDialogAction: DismissDialogAction) => {
      console.info("reason=" + JSON.stringify(dismissDialogAction.reason))
      console.log("dialog onWillDismiss")
      if (dismissDialogAction.reason == DismissReason.PRESS_BACK) {
        dismissDialogAction.dismiss()
      }
      if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) {
        dismissDialogAction.dismiss()
      }
    },
    autoCancel: true,
    customStyle: true,
  });

在需要展示弹窗的时候调用customDialogController的open方法。

 if (this.customDialogController != null) {
                      this.customDialogController.open()
                    }

当然如果页面消失,尽量在aboutToDisappear中将customDialogController置空

  // 在自定义组件即将销毁时将dialogController置空
  aboutToDisappear() {
    this.customDialogController = null // 将dialogController置空
  }

三、Overlay浮层

在官方文档中有一段描述
浮层(OverlayManager) 用于将自定义的UI内容展示在页面(Page)之上,在Dialog、Popup、Menu、BindSheet、BindContentCover和Toast等组件之下,展示的范围为当前窗口安全区内。可适用于常驻悬浮等场景。

使用OverlayManager来添加、删除、隐藏、显示节点Component

效果预览
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1.定义CustomOverlayView组件界面

在CustomOverlayView中,我们定义了加载中,加载失败,加载成功的几种类型,用于展示不同的样式
定义OverlayConfig类为展示的界面配置、OverlayScaleImage缩放的icon

export enum OverlayType {
  loading,
  success,
  fail
}

export class OverlayConfig {
  message: string = ""
  offset: Position = { x: 0, y: -50 }
  index: number = 0
  autoDismiss: boolean = true;
  duration: number = 3000 // 持续时间
  onCallback?: (index: number) => void
  type: OverlayType = OverlayType.loading

  constructor(message: string) {
    this.message = message
  }
}

@Builder
export function builderCustomOverlayView(overlayConfig: OverlayConfig) {
  CustomOverlayView({
    olConfig: overlayConfig
  })
}

@ComponentV2
struct OverlayScaleImage {
  @Param @Require src: PixelMap | ResourceStr | DrawableDescriptor;
  @Param imgWidth: number = 40;
  @Local imgScale: number = 0.0;

  build() {
    Image(this.src)
      .width(this.imgWidth)
      .aspectRatio(1)
      .scale({ x: this.imgScale, y: this.imgScale })
      .animation({
        duration: 300, // 时长
        iterations: 1, // 设置-1表示动画无限循环
      })
      .onAppear(() => {
        // 组件挂载完毕,修改数值触发动画效果
        this.imgScale = 1.0
      })
  }
}

@ComponentV2
export struct CustomOverlayView {
  @Param olConfig: OverlayConfig = new OverlayConfig("");

  aboutToAppear(): void {
    setTimeout(() => {
      console.log("CustomOverlayView aboutToAppear");
      if (this.olConfig.onCallback != null) {
        this.olConfig.onCallback(this.olConfig.index);
      }
    }, this.olConfig.duration);
  }

  build() {
    Column() {
      if (OverlayType.loading == this.olConfig.type) {
        // message
        LoadingProgress()
          .color($r('app.color.success'))
          .width(40)
          .height(40)
      } else if (OverlayType.success == this.olConfig.type) {
        // message
        OverlayScaleImage({
          src: $r('app.media.ic_hud_success'),
          imgWidth: 40
        })
      } else if (OverlayType.fail == this.olConfig.type) {
        // message
        OverlayScaleImage({
          src: $r('app.media.ic_hud_fail'),
          imgWidth: 30
        })
      }

      Text(this.olConfig.message)
        .fontSize(14)
        .fontColor($r('app.color.white'))
        .margin({
          top: 10,
        })
    }
    .padding({
      top: 20,
      bottom: 20,
      left: 15,
      right: 15
    })
    .justifyContent(FlexAlign.Center)
    .constraintSize({
      minWidth: 180,
      minHeight: 80,
    })
    .backgroundColor($r('app.color.overlay_bg_color'))
    .borderRadius(10)
    .offset(this.olConfig.offset)
  }
}
  • 2.CustomOverlayStorage

由于在OverlayManager来添加、删除、隐藏、显示节点过程中,需要使用index索引参数。这里使用一个类,类中有一个数组记录一下展示的节点Component

@ObservedV2
export class CustomOverlayStorage {
  @Trace contentArray: ComponentContent<OverlayConfig>[] = []
}
  • 3.自定义MyOverlayManager进行封装OverlayManager

首先确定属性uiContext,创建ComponentContent需要该参数,这个我在index.ets中进行初始化传入。
CustomOverlayStorage存储ComponentContent的数组,确定index
overlayManager用来来添加、删除、隐藏、显示节点

MyOverlayManager代码如下

/// 用于管理Overlay
/// 浮层(OverlayManager) 用于将自定义的UI内容展示在页面(Page)之上,
/// 在Dialog、Popup、Menu、BindSheet、BindContentCover和Toast等组件之下,
/// 展示的范围为当前窗口安全区内。可适用于常驻悬浮等场景。
/// 与OverlayManager相关的属性推荐采用AppStorage来进行应用全局存储,以免切换页面后属性值发生变化从而导致业务错误。
import { AppStorageV2, ComponentContent, OverlayManager, router } from '@kit.ArkUI';
import {
  builderCustomOverlayView,
  CustomOverlayStorage,
  OverlayConfig
} from '../common/components/hud/CustomOverlayView';

export class MyOverlayManager {
  private static currentIndex: number = 0;
  private uiContext?: UIContext
  private overlayManager?: OverlayManager
  private overlayStorage: CustomOverlayStorage =
    AppStorageV2.connect(CustomOverlayStorage, 'overlayStorage', () => new CustomOverlayStorage())!;
  private static instance: MyOverlayManager;

  public static getInstance(): MyOverlayManager {
    if (MyOverlayManager.instance == null) {
      MyOverlayManager.instance = new MyOverlayManager();
    }
    return MyOverlayManager.instance;
  }

  initOverlayNode(uiContext: UIContext): void {
    this.uiContext = uiContext;
    this.overlayManager = uiContext.getOverlayManager();
  }

  addOverlayView(overlayConfig: OverlayConfig): void {
    if (this.uiContext != null && this.uiContext != undefined) {
      // 设置索引下标
      let index = MyOverlayManager.currentIndex++;
      overlayConfig.index = index;

      // 创建componentContent
      let componentContent = new ComponentContent(
        this.uiContext!, wrapBuilder<[OverlayConfig]>(builderCustomOverlayView),
        overlayConfig
      )

      this.overlayStorage.contentArray.push(componentContent);

      if (this.overlayManager != null && this.overlayManager != undefined) {
        this.overlayManager.addComponentContent(componentContent, index)
      }
    }
  }

  hideOverlayView(index: number) {
    if (this.overlayManager != null && this.overlayManager != undefined) {
      if (index < this.overlayStorage.contentArray.length) {
        this.overlayManager.hideComponentContent(this.overlayStorage.contentArray[index])
      }
    }
  }

  showOverlayView(index: number) {
    if (this.overlayManager != null && this.overlayManager != undefined) {
      if (index < this.overlayStorage.contentArray.length) {
        this.overlayManager.showComponentContent(this.overlayStorage.contentArray[index])
      }
    }
  }

  removeOverlayView(index: number) {
    if (this.overlayManager != null && this.overlayManager != undefined) {
      if (index < this.overlayStorage.contentArray.length) {
        this.overlayManager.removeComponentContent(this.overlayStorage.contentArray[index])
      }
    }
  }

  removeAllOverlayView() {
    if (this.overlayManager != null && this.overlayManager != undefined) {
      this.overlayManager.hideAllComponentContents();
      for (let index: number = 0; index < this.overlayStorage.contentArray.length; index++) {
        this.overlayManager.removeComponentContent(this.overlayStorage.contentArray[index])
      }
    }
  }
}
  • 4.index.ets传入uiContext

初始化配置uiContext

aboutToAppear(): void {
    console.log("aboutToAppear");
    MyOverlayManager.getInstance().initOverlayNode(this.getUIContext());
  }

  • 5.调用OverlayManager进行显示加载中、加载成功、加载失败提示

定义type及message

let config = new OverlayConfig(message);
    config.type = OverlayType.loading;
    config.onCallback = (index: number)=>{
      MyOverlayManager.getInstance().removeOverlayView(index)
    }
    MyOverlayManager.getInstance().addOverlayView(config);

加载中、加载成功、加载失败的Util

import { MyOverlayManager } from "../../manager/MyOverlayManager";
import { OverlayConfig, OverlayType } from "../components/hud/CustomOverlayView";

export class EasyLoadingHud {
  static showLoading(message: string) {
    let config = new OverlayConfig(message);
    config.type = OverlayType.loading;
    config.onCallback = (index: number)=>{
      MyOverlayManager.getInstance().removeOverlayView(index)
    }
    MyOverlayManager.getInstance().addOverlayView(config);
  }

  static showSuccess(message: string) {
    let config = new OverlayConfig(message);
    config.type = OverlayType.success;
    config.onCallback = (index: number)=>{
      MyOverlayManager.getInstance().removeOverlayView(index)
    }
    MyOverlayManager.getInstance().addOverlayView(config);
  }

  static showFail(message: string) {
    let config = new OverlayConfig(message);
    config.type = OverlayType.fail;
    config.onCallback = (index: number)=>{
      MyOverlayManager.getInstance().removeOverlayView(index)
    }
    MyOverlayManager.getInstance().addOverlayView(config);
  }
}

  • 6.页面调用EasyLoadingHud进行显示

可以在页面需要的地方调用EasyLoadingHud进行显示

代码如下

// 加载中
EasyLoadingHud.showLoading("加载中...")
// 加载成功
EasyLoadingHud.showSuccess("加载成功")
// 加载失败
EasyLoadingHud.showFail("加载失败")

四、小结

在开发过程中会遇到提示弹窗、加载中指示器、加载失败的toast等功能,这里是学习HarmonyOS鸿蒙开发的学习记录,如果对你有用,你可以点个赞哦~~。详细的文档还是以官方文档为主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/953363.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STL之VectorMapList针对erase方法踩坑笔记

前沿 如下总结的三种容器&#xff0c;开头都会涉及当前容器的特点&#xff0c;再者就本次针对erase方法的使用避坑总结。 一.Vector vector关联关联容器&#xff0c;存储内存是连续&#xff0c;且特点支持快速访问&#xff0c;但是插入和删除效率比较地(需要找查找和移动)。另…

【Rust】引用与借用

目录 思维导图 1. 引用与借用的基本概念 1.1. 引用示例 2. 借用的规则 2.1. 可变借用示例 2.2. 借用的限制 3. 引用的生命周期 思维导图 1. 引用与借用的基本概念 引用的定义&#xff1a;引用是一种指向数据的指针&#xff0c;但与裸指针不同&#xff0c;Rust的引用在编…

《自动驾驶与机器人中的SLAM技术》ch8:基于 IESKF 的紧耦合 LIO 系统

紧耦合系统&#xff0c;就是把点云的残差方程直接作为观测方程&#xff0c;写入观测模型中。这种做法相当于在滤波器或者优化算法内置了一个 ICP 或 NDT。因为 ICP 和 NDT 需要迭代来更新它们的最近邻&#xff0c;所以相应的滤波器也应该使用可以迭代的版本&#xff0c;ESKF 对…

Mac 删除ABC 输入法

参考链接&#xff1a;百度安全验证 Mac下删除系统自带输入法ABC&#xff0c;正解&#xff01;_mac删除abc输入法-CSDN博客 ABC 输入法和搜狗输入法等 英文有冲突~~ 切换后还会在英文状态&#xff0c;可以删除 &#xff1b;可能会对DNS 输入有影响&#xff0c;但是可以通过复…

1.13 多线程编程

1.思维导图 2.创建两个子进程&#xff0c;父进程负责&#xff1a;向文件中写入数据&#xff1b;两个子进程负责&#xff1a;从文件中读取数据。 要求&#xff1a;一定保证1号子进程先读取&#xff0c;2号子进程后读取&#xff0c;使用文件IO去实现。 1>程序代码 …

Elasticsearch ES|QL 地理空间索引加入纽约犯罪地图

可以根据地理空间数据连接两个索引。在本教程中&#xff0c;我将向你展示如何通过混合邻里多边形和 GPS 犯罪事件坐标来创建纽约市的犯罪地图。 安装 如果你还没有安装好自己的 Elasticsearch 及 Kibana 的话&#xff0c;请参考如下的链接来进行安装。 如何在 Linux&#xff0…

数据分析-使用Excel透视图/表分析禅道数据

背景 禅道&#xff0c;是目前国内用得比较多的研发项目管理系统&#xff0c;我们常常会用它进行需求管理&#xff0c;缺陷跟踪&#xff0c;甚至软件全流程的管理&#xff0c;如果能将平台上的数据结公司的实际情况进行合理的分析利用&#xff0c;相信会给我们的项目复盘总结带来…

【c语言】指针 (完结)

一、sizeof和strlen的对比 1、sizeof 前面我们在学习操作符的时候&#xff0c;我们学习了sizeof&#xff0c;知道其是计算变量所占内存的大小的&#xff0c;单 位是字节&#xff0c;如果操作数是数据类型的话&#xff0c;计算的就是这个类型的变量所占的内存空间的大…

Chromium 132 编译指南 Windows 篇 - 生成构建文件 (六)

1. 引言 在上一篇文章中&#xff0c;我们已经成功获取了 Chromium 的源代码并同步了相关的第三方依赖。本文将继续深入&#xff0c;指导您如何使用 GN 工具生成构建文件&#xff0c;为接下来的编译工作奠定基础。 2. 切换 Chromium 版本至 132 在开始正式构建之前&#xff0…

(12)springMVC文件的上传

SpringMVC文件上传 首先是快速搭建一个springMVC项目 新建项目mvn依赖导入添加webMoudle添加Tomcat运行环境.在配置tomcat时ApplicationContext置为"/"配置Artfact的lib配置WEB-INF配置文件&#xff08;记得添加乱码过滤&#xff09;配置springmvc-servlet文件&…

3D目标检测数据集——Waymo数据集

Waymo数据集簡介 发布首页&#xff1a;https://waymo.com/open/ 论文&#xff1a;https://openaccess.thecvf.com/content_CVPR_2020/papers/Sun_Scalability_in_Perception_for_Autonomous_Driving_Waymo_Open_Dataset_CVPR_2020_paper.pdf github&#xff1a;https://github.…

Mysql--运维篇--空间管理(表空间,索引空间,临时表空间,二进制日志,数据归档等)

MySQL的空间管理是指对数据库存储资源的管理和优化。确保数据库能够高效地使用磁盘空间、内存和其他系统资源。良好的空间管理不仅有助于提高数据库的性能&#xff0c;还能减少存储成本并防止因磁盘空间不足导致的服务中断。MySQL的空间管理涉及多个方面&#xff0c;包括表空间…

STM32之LWIP网络通讯设计-下(十五)

STM32F407 系列文章 - ETH-LWIP&#xff08;十五&#xff09; 目录 前言 一、软件设计 二、CubeMX实现 1.配置前准备 2.CubeMX配置 1.ETH模块配置 2.时钟模块配置 3.中断模块配置 4.RCC及SYS配置 5.LWIP模块配置 3.生成代码 1.main文件 2.用户层源文件 3.用户层头…

Gateway 网关

1.Spring Cloud Gateway Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关&#xff0c;Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式&#xff0c;Spring Cloud Gateway作为Spring Cloud生态…

数据结构:栈(Stack)和队列(Queue)—面试题(二)

1. 用队列实现栈。 习题链接https://leetcode.cn/problems/implement-stack-using-queues/description/描述&#xff1a; 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&a…

在 .NET 9 中使用 Scalar 替代 Swagger

前言 在.NET 9发布以后ASP.NET Core官方团队发布公告已经将Swashbuckle.AspNetCore&#xff08;一个为ASP.NET Core API提供Swagger工具的项目&#xff09;从ASP.NET Core Web API模板中移除&#xff0c;这意味着以后我们创建Web API项目的时候不会再自动生成Swagger API文档了…

双模充电桩发展前景:解锁新能源汽车未来的金钥匙,市场潜力无限

随着全球能源转型的浪潮席卷而来&#xff0c;新能源汽车行业正以前所未有的速度蓬勃发展&#xff0c;而作为其坚实后盾的充电基础设施&#xff0c;特别是双模充电桩&#xff0c;正逐渐成为推动这一变革的关键力量。本文将从多维度深入剖析双模充电桩的市场现状、显著优势、驱动…

Notepad++上NppFTP插件的安装和使用教程

一、NppFTP插件下载 图示是已经安装好了插件。 在搜索框里面搜NppFTP&#xff0c;一般情况下&#xff0c;自带的下载地址容易下载失败。这里准备了一个下载连接&#xff1a;Release v0.29.10 ashkulz/NppFTP GitHub 这里我下载的是x86版本 下载好后在nodepad的插件里面选择打…

Mysql--运维篇--备份和恢复(逻辑备份,mysqldump,物理备份,热备份,温备份,冷备份,二进制文件备份和恢复等)

MySQL 提供了多种备份方式&#xff0c;每种方式适用于不同的场景和需求。根据备份的粒度、速度、恢复时间和对数据库的影响&#xff0c;可以选择合适的备份策略。主要备份方式有三大类&#xff1a;逻辑备份&#xff08;mysqldump&#xff09;&#xff0c;物理备份和二进制文件备…

在 Safari 浏览器中,快速将页面恢复到 100% 缩放(也就是默认尺寸)Command (⌘) + 0 (零)

在 Safari 浏览器中&#xff0c;没有一个专门的快捷键可以将页面恢复到默认的缩放比例。 但是&#xff0c;你可以使用以下两种方法快速将页面恢复到 100% 缩放&#xff08;也就是默认尺寸&#xff09;&#xff1a; 方法一&#xff1a;使用快捷键 (最常用) Command (⌘) 0 (零…