鸿蒙5.0开发进阶:ArkTS组件-元服务(NavPushPathHelper)

鸿蒙5.0开发:NavPushPathHelper组件详解

往期鸿蒙5.0全套实战文章必看:(文中附带全栈鸿蒙5.0学习资料)


NavPushPathHelper

当跳转的目标NavDestination在不同的hsp分包,且未被主包依赖,首次运行元服务只会下载安装主包,需要使用NavPushPathHelper先下载安装相应hsp分包,再将指定的NavDestination页面信息入栈。使Navigation支持动态加载hsp分包后再跳转。

说明

该组件从API Version 12开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。

导入模块

import { NavPushPathHelper } from '@kit.ArkUI'

子组件

属性

不支持通用属性

对Navigation路由栈NavPathStack的所有路由跳转接口进行了封装,在NavPushPathHelper中持有一个NavPathStack对象,在封装的跳转接口中,去判断子包是否存在,如果不存在则进行动态下载子包,等结果返回后调用NavPathStack的相应的接口将指定的NavDestination页面信息入栈。

constructor

constructor(navPathStack: NavPathStack)

NavPushPathHelper的构造函数。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
navPathStackNavPathStackNavigation路由栈。

pushPath

pushPath(moduleName: string, info: NavPathInfo, animated?: boolean): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将info指定的NavDestination页面信息入栈,使用Promise异步回调返回接口调用结果。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
infoNavPathInfoNavDestination页面的信息。
animatedboolean是否支持转场动画,默认值:true。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
300001hsp silent install fail.

pushPath

pushPath(moduleName: string, info: NavPathInfo, options?: NavigationOptions): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将info指定的NavDestination页面信息入栈,使用Promise异步回调返回接口调用结果,具体根据options中指定不同的LaunchMode,有不同的行为。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
infoNavPathInfoNavDestination页面的信息。
optionsNavigationOptions页面栈操作选项。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
300001hsp silent install fail.

pushPathByName

pushPathByName(moduleName: string, name: string, param: Object, animated?: boolean): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将name指定的NavDestination页面信息入栈,传递的数据为param,使用Promise异步回调返回接口调用结果。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
namestringNavDestination页面名称。
paramObjectNavDestination页面详细参数。
animatedboolean是否支持转场动画,默认值:true。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
300001hsp silent install fail.

pushPathByName

pushPathByName(moduleName: string, name: string, param: Object, onPop: Callback<PopInfo>, animated?: boolean): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将name指定的NavDestination页面信息入栈,传递的数据为param,添加onPop回调接收入栈页面出栈时的返回结果,并进行处理,使用Promise异步回调返回接口调用结果。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
namestringNavDestination页面名称。
paramObjectNavDestination页面详细参数。
onPopCallback<PopInfo>Callback回调,用于页面出栈时触发该回调处理返回结果。
animatedboolean是否支持转场动画,默认值:true。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
300001hsp silent install fail.

pushDestination

pushDestination(moduleName: string, info: NavPathInfo, animated?: boolean): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将info指定的NavDestination页面信息入栈,使用Promise异步回调返回接口调用结果。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
infoNavPathInfoNavDestination页面的信息。
animatedboolean是否支持转场动画,默认值:true。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified. 2. Incorrect parameters types. 3. Parameter verification failed.
100001Internal error.
100005Builder function not registered.
100006NavDestination not found.
300001hsp silent install fail.

pushDestination

pushDestination(moduleName: string, info: NavPathInfo, options?: NavigationOptions): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将info指定的NavDestination页面信息入栈,使用Promise异步回调返回接口调用结果,具体根据options中指定不同的LaunchMode,有不同的行为。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
infoNavPathInfoNavDestination页面的信息。
optionsNavigationOptions页面栈操作选项。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified. 2. Incorrect parameters types. 3. Parameter verification failed.
100001Internal error.
100005Builder function not registered.
100006NavDestination not found.
300001hsp silent install fail.

pushDestinationByName

pushDestinationByName(moduleName: string, name: string, param: Object, animated?: boolean): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将name指定的NavDestination页面信息入栈,传递的数据为param,使用Promise异步回调返回接口调用结果。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
namestringNavDestination页面名称。
paramObjectNavDestination页面详细参数。
animatedboolean是否支持转场动画,默认值:true。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified. 2. Incorrect parameters types. 3. Parameter verification failed.
100001Internal error.
100005Builder function not registered.
100006NavDestination not found.
300001hsp silent install fail.

pushDestinationByName

pushDestinationByName(moduleName: string, name: string, param: Object, onPop: Callback<PopInfo>, animated?: boolean): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将name指定的NavDestination页面信息入栈,传递的数据为param,并且添加用于页面出栈时处理返回结果的OnPop回调,使用Promise异步回调返回接口调用结果。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
namestringNavDestination页面名称。
paramObjectNavDestination页面详细参数。
onPopCallback<PopInfo>Callback回调,用于页面出栈时处理返回结果。
animatedboolean是否支持转场动画,默认值:true。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified. 2. Incorrect parameters types. 3. Parameter verification failed.
100001Internal error.
100005Builder function not registered.
100006NavDestination not found.
300001hsp silent install fail.

replacePath

replacePath(moduleName: string, info: NavPathInfo, animated?: boolean): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将当前页面栈栈顶退出,将info指定的NavDestination页面信息入栈,使用Promise异步回调返回接口调用结果。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
infoNavPathInfo新栈顶页面参数信息
animatedboolean是否支持转场动画,默认值:true。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
300001hsp silent install fail.

replacePath

replacePath(moduleName: string, info: NavPathInfo, options?: NavigationOptions): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将当前页面栈栈顶退出,使用Promise异步回调返回接口调用结果,具体根据options中指定不同的LaunchMode,有不同的行为。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
infoNavPathInfo新栈顶页面参数信息。
optionsNavigationOptions页面栈操作选项。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
300001hsp silent install fail.

replacePathByName

replacePathByName(moduleName: string, name: string, param: Object, animated?: boolean): Promise<void>

先判断分包是否存在,若不存在,则通过moduleName下载分包,再将当前页面栈栈顶退出,将name指定的页面入栈,使用Promise异步回调返回接口调用结果。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名类型必填说明
moduleNamestring目标NavDestination所在分包的moduleName。
namestringNavDestination页面名称。
paramObjectNavDestination页面详细参数。
animatedboolean是否支持转场动画,默认值:true。

返回值:

类型说明
Promise<void>异常返回结果。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
300001hsp silent install fail.

事件

不支持通用事件

示例

主包:

// Index.ets
import { NavPushPathHelper } from '@kit.ArkUI'
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct NavigationExample {
  pageInfo: NavPathStack = new NavPathStack()
  helper: NavPushPathHelper = new NavPushPathHelper(this.pageInfo)

  build() {
    Navigation(this.pageInfo) {
      Column() {
        Button('StartTest', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(40)
          .margin(20)
          .onClick(() => {
            this.helper.pushPath('hsptest1', { name: 'pageOne' }, false)
              .catch((error: BusinessError) => {
              console.error(`[pushPath]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.error('[pushPath]success.');
            }); // 将name指定的NavDestination页面信息入栈。
          })
      }
    }.title('NavIndex')
  }
}

分包hsptest1:

// PageOne.ets
import { NavPushPathHelper } from '@kit.ArkUI'
import { BusinessError } from '@kit.BasicServicesKit';

class TmpClass {
  count: number = 10
}

class ParamWithOp {
  operation: number = 1
  count: number = 10
}

@Builder
export function PageOneBuilder(name: string, param: Object) {
  PageOne()
}

@Component
export struct PageOne {
  pageInfo: NavPathStack = new NavPathStack();
  helper: NavPushPathHelper = new NavPushPathHelper(this.pageInfo)
  @State message: string = 'Hello World'

  build() {
    NavDestination() {
      Column() {
        Text(this.message)
          .width('80%')
          .height(50)
          .margin(10)

        Button('pushPath', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            this.helper.pushPath('hsptest2', { name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo) => {
              this.message = '[pushPath]last page is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result);
            }}).catch((error: BusinessError) => {
              console.error(`[pushPath]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.log('[pushPath]success.');
            });
          })

        Button('pushPath with NavigationOptions', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            this.helper.pushPath('hsptest2', { name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo) => {
              this.message = '[pushPath with NavigationOptions]last page is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result);
            }}, {launchMode:0, animated:true}).catch((error: BusinessError) => {
              console.error(`[pushPath with NavigationOptions]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.log('[pushPath with NavigationOptions]success.');
            });
          })

        Button('pushPathByName', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            let tmp = new TmpClass()
            this.helper.pushPathByName('hsptest2', 'pageTwo', tmp, (popInfo) => {
              this.message = '[pushPathByName]last page is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result);
            }).catch((error: BusinessError) => {
              console.error(`[pushPathByName]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.log('[pushPathByName]success.');
            });
          })

        Button('pushPathByNameWithoutOnPop', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            let tmp = new TmpClass()
            this.helper.pushPathByName('hsptest2', 'pageTwo', tmp, true)
            .catch((error: BusinessError) => {
              console.error(`[pushPathByNameWithoutOnPop]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.log('[pushPathByNameWithoutOnPop]success.');
            });
          })

        Button('pushDestination', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            let tmp = new TmpClass()
            this.helper.pushDestination('hsptest2', { name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo) => {
              this.message = '[pushDestination]last page is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result);
            }}).catch((error: BusinessError) => {
              console.error(`[pushDestination]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.error('[pushDestination]success.');
            });
          })

        Button('pushDestination with NavigationOptions', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            let tmp = new TmpClass()
            this.helper.pushDestination('hsptest2', { name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo) => {
              this.message = '[pushDestination with NavigationOptions]last page is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result);
            }}, {launchMode:0, animated:true}).catch((error: BusinessError) => {
              console.error(`[pushDestination with NavigationOptions]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.error('[pushDestination with NavigationOptions]success.');
            });
          })

        Button('pushDestinationByName', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            let tmp = new TmpClass()
            this.helper.pushDestinationByName('hsptest2','pageTwo', tmp, (popInfo) => {
              this.message = '[pushDestinationByName]last page is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result);
            }).catch((error: BusinessError) => {
              console.error(`[pushDestinationByName]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.error('[pushDestinationByName]success.');
            });
          })

        Button('pushDestinationByNameWithoutOnPop', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            let tmp = new TmpClass()
            this.helper.pushDestinationByName('hsptest2','pageTwo', tmp, true)
              .catch((error: BusinessError) => {
                console.error(`[pushDestinationByNameWithoutOnPop]failed, error code = ${error.code}, error.message = ${error.message}.`);
              }).then(() => {
              console.error('[pushDestinationByNameWithoutOnPop]success.');
            });
          })

        Button('replacePath', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            this.helper.replacePath('hsptest2', { name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo) => {
              this.message = '[replacePath]last page is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result);
            }}).catch((error: BusinessError) => {
              console.error(`[replacePath]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.log('[replacePath]success.');
            });
          })

        Button('replacePath with NavigationOptions', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            this.helper.replacePath('hsptest2', { name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo) => {
              this.message = '[replacePath with NavigationOptions]last page is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result);
            }}, {launchMode:0, animated:true}).catch((error: BusinessError) => {
              console.error(`[replacePath with NavigationOptions]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.log('[replacePath with NavigationOptions]success.');
            });
          })

        Button('replacePathByName', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(35)
          .margin(10)
          .onClick(() => {
            let tmp = new TmpClass()
            this.helper.replacePathByName('hsptest2', 'pageTwo', tmp)
              .catch((error: BusinessError) => {
              console.error(`[replacePathByName]failed, error code = ${error.code}, error.message = ${error.message}.`);
            }).then(() => {
              console.log('[replacePathByName]success.');
            });
          })

      }.width('100%').height('100%')
    }.title('pageOne')
    .onBackPressed(() => {
      this.pageInfo.pop({ number: 1 }) // 弹出路由栈栈顶元素。
      return true
    }).onReady((context: NavDestinationContext) => {
      this.pageInfo = context.pathStack;
      this.helper = new NavPushPathHelper(this.pageInfo);
    })
  }
}
// 工程配置文件module.json5中配置 {"routerMap": "$profile:route_map"}
// route_map.json
{
  "routerMap": [
    {
      "name": "pageOne",
      "pageSourceFile": "src/main/ets/pages/PageOne.ets",
      "buildFunction": "PageOneBuilder",
      "data": {
        "description": "this is pageOne"
      }
    }
  ]
}

分包hsptest2:

// PageTwo.ets

class resultClass {
  constructor(count: number) {
    this.count = count;
  }
  count: number = 10
}

@Builder
export function PageTwoBuilder() {
  PageTwo()
}

@Component
export struct PageTwo {
  pathStack: NavPathStack = new NavPathStack()

  build() {
    NavDestination() {
      Column() {
        Button('pop', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(40)
          .margin(20)
          .onClick(() => {
            this.pathStack.pop(new resultClass(1)); // 回退到上一个页面,将处理结果传入push的onPop回调中。
          })
      }.width('100%').height('100%')
    }.title('pageTwo')
    .onBackPressed(() => {
      this.pathStack.pop(new resultClass(0)); // 回退到上一个页面,将处理结果传入push的onPop回调。
      return true;
    }).onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack
    })
  }
}
// 工程配置文件module.json5中配置 {"routerMap": "$profile:route_map"}
// route_map.json
{
  "routerMap": [
    {
      "name": "pageTwo",
      "pageSourceFile": "src/main/ets/pages/PageTwo.ets",
      "buildFunction": "PageTwoBuilder",
      "data": {
        "description": "this is pageTwo"
      }
    }
  ]
}

HarmonyOS 5.0ArkTS 组件中,设置触摸热区(Touch Area)属性可以通过组件的 `touchable` 和 `hitTestBehavior` 等属性来实现。这些属性允许开发者定义组件响应触摸事件的区域和行为。 ### 配置触摸热区属性 #### 1. `touchable` 属性 `touchable` 属性用于控制组件是否可以响应触摸事件。设置为 `true` 时,组件将可以响应触摸事件;设置为 `false` 时,组件将忽略所有触摸事件。 ```ts @Component struct MyComponent { build() { Column() { Text('Click Me') .fontSize(30) .touchable(true) } .width('100%') .height(100) } } ``` #### 2. `hitTestBehavior` 属性 `hitTestBehavior` 属性用于定义组件在命中测试(Hit Test)中的行为。它有以下几种取值: - `HitTestBehavior.self`: 组件仅在其自身的区域内响应触摸事件。 - `HitTestBehavior.none`: 组件不响应任何触摸事件。 - `HitTestBehavior.default`: 组件按照默认行为响应触摸事件。 ```ts @Component struct MyComponent { build() { Column() { Text('Click Me') .fontSize(30) .hitTestBehavior(HitTestBehavior.self) } .width('100%') .height(100) } } ``` #### 3. 自定义触摸热区 如果需要更复杂的触摸热区配置,可以通过自定义组件的 `onTouch` 事件来实现。通过监听触摸事件,开发者可以自定义组件的响应逻辑。 ```ts @Component struct MyComponent { @State message: string = 'Touch Me' build() { Column() { Text(this.message) .fontSize(30) .onTouch((event: TouchEvent) => { if (event.type === TouchType.Down) { this.message = 'Touched Down' } else if (event.type === TouchType.Up) { this.message = 'Touched Up' } }) } .width('100%') .height(100) } } ``` ### 注意事项 - **性能优化**:在设置触摸热区时,应避免不必要的复杂逻辑,以确保应用的响应速度和性能。 - **交互设计**:触摸热区的设计应符合用户习惯,避免过于敏感或响应区域过小的问题。 通过以上方法,开发者可以灵活地配置 ArkTS 组件的触摸热区属性,以满足不同的交互需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值