HarmonyOS开发——uni-app 手机号一键登录鸿蒙端(HarmonyOS NEXT)适配全记录

手机号一键登录是提升用户转化率的关键功能。在 Android / iOS 端,uni-app 通过 uni.login({ provider: 'univerify' }) 即可唤起运营商授权弹窗,用户一键完成登录。但在 HarmonyOS NEXT 上,这套方案从平台开通、配置对齐到 API 调用都经历了完全不同的实现路径。

本文将完整记录鸿蒙端适配一键登录的全过程:从最初的 缺少参数: appid or gyid,到 当前应用AppId尚未开通uni一键登录,再到最终跑通全流程。

一、先开通平台服务:uni-app 一键登录配置

鸿蒙端一键登录能否跑通,第一步不是写代码,而是先把 uni-app 平台侧的一键登录服务开通、计费方式确认、AppID 对齐。否则客户端配置再正确,也很容易遇到:

{ "errSubject": "uni-verify", "errCode": 4000, "errMsg": "缺少参数: appid or gyid" }

或者:

{ "errMsg": "当前应用AppId尚未开通uni一键登录" }

1. 进入 DCloud 开发者中心

登录 DCloud 开发者中心,进入应用管理页面。

这里需要注意:鸿蒙端必须使用独立的鸿蒙应用配置。不能直接把 Android / iOS 的 AppID 拿过来复用,因为鸿蒙原生工程的包名、签名、SDK 读取方式都与 Android / iOS 不同。

建议操作路径:

  1. 打开 DCloud 开发者中心;
  2. 找到已有应用,或新建一个鸿蒙端专用应用;
  3. 确认应用包名与鸿蒙工程 bundleName 一致;
  4. 记录该应用的 DCloud AppID,例如 __UNI__XXXXXXX。

[配图:DCloud 应用列表页]

[配图:鸿蒙端应用详情页]

2. 开通 uni 一键登录服务

在应用详情中找到 uni 一键登录 / univerify 相关服务入口,点击开通。

这里要重点确认三件事:

检查项 说明 常见问题
是否已开通 未开通时客户端会提示 AppId 未开通 最容易忽略
开通的是哪个 AppID 必须与鸿蒙端使用的 AppID 一致 误开在 Android/iOS 应用下
计费/套餐状态 一键登录通常按成功调用计费 欠费或套餐异常会影响调用

[配图:uni 一键登录服务开通页]

[配图:计费/套餐状态页]

3. 确认包名与应用绑定

鸿蒙端一键登录依赖原生工程配置,因此包名必须严格一致。

建议核对:

  • DCloud 后台应用包名;
  • 鸿蒙工程 bundleName;
  • module.json5 中的 metadata;
  • 客户端 manifest.json 中的 appid;
  • 云函数 getPhoneNumber 中的 appid。

只要其中一处不一致,就可能出现参数缺失或应用未开通的错误。

二、鸿蒙端与 Android/iOS 的核心差异

维度 Android/iOS HarmonyOS NEXT
调用方式 uni.login({ provider: 'univerify' }) uni.getUniverifyManager().login()
底层 SDK DCloud 内置 @getui/gysdk(个推 SDK)
AppID 读取位置 manifest.json 内置 module.json5 metadata 中的 GETUI_APPID
关闭弹窗 uni.closeAuthView() manager.close()
样式支持 丰富(按钮颜色、隐私条款、关闭按钮等) 有限(仅 fullScreen / logoPath / loginBtnText)
预登录 隐式(框架内部处理) 显式(需手动调用 preLogin)

三、配置清单:四处必须对齐的 AppID

3.1 DCloud 后台

在 DCloud 开发者中心创建鸿蒙应用,包名填鸿蒙原生工程的 bundleName,开通 uni 一键登录 服务,获得新的 DCloud AppID。

重要:鸿蒙端包名与 Android/iOS 不同,必须在 DCloud 后台新建应用,不能复用 Android/iOS 的 AppID。

3.2 manifest.json(uni-app 工程)

{
  "appid": "__UNI__YOUR_HARMONY_APPID",
  "privacy": {
    "provider": "univerify",
    "univerify": {
      "termsUrl": "https://your-domain.com/userAgreement.html",
      "privacyUrl": "https://your-domain.com/privAgreement.htm"
    }
  },
  "app-harmony": {
    "distribute": {
      "modules": {
        "uni-verify": {
          "appid": "__UNI__YOUR_HARMONY_APPID"
        }
      }
    }
  }
}

[配图:manifest.json 中的 app-harmony.modules.uni-verify.appid]

3.3 module.json5(鸿蒙原生工程)

这是最关键的一步——个推 SDK 从 module.json5 的 metadata 中读取 AppID:

{
    module: {
        metadata: [
            {
                name: "GETUI_APPID",
                value: "__UNI__YOUR_HARMONY_APPID",
            },
        ],
    },
}

踩坑:如果不配置 GETUI_APPID,个推 SDK 初始化时会报错 缺少参数: appid or gyid。

[配图:module.json5 中的 GETUI_APPID]

3.4 云函数 getPhoneNumber

'use strict';
exports.main = async (event, context) => {
  const res = await uniCloud.getPhoneNumber({
    appid: '__UNI__YOUR_HARMONY_APPID',
    provider: 'univerify',
    access_token: event.access_token,
    openid: event.openid,
  });
  return res;
};

云函数的 package.json 需要声明 uni-cloud-verify 扩展:

{
  "name": "getPhoneNumber",
  "extensions": {
    "uni-cloud-jql": {},
    "uni-cloud-verify": {}
  }
}

[配图:云函数 getPhoneNumber 配置页]

四、踩坑实录:从报错到跑通

4.1 第一个坑:缺少参数: appid or gyid

现象:调用 preLogin 时直接报错:

{ "errSubject": "uni-verify", "errCode": 4000, "errMsg": "缺少参数: appid or gyid" }

排查过程:

  1. 最初在编译产物的 manifest.json 中添加了 distribute.modules.uni-verify.appid → 不生效
  2. 深入分析 @getui/gysdk 的类型声明文件,发现关键线索:
// GyConsts.d.ets
export declare class GyConsts {
    static readonly GETUI_APPID = "GETUI_APPID";
}
  1. 个推 SDK 是从 HarmonyOS 的 module.json5 metadata 中读取 GETUI_APPID 的,而不是从 manifest.json

修复:在 module.json5 的 metadata 中添加 GETUI_APPID。

4.2 第二个坑:当前应用AppId尚未开通uni一键登录

现象:添加 GETUI_APPID 后,错误变了——说明 SDK 配置已生效,但 DCloud 后台的应用没有开通鸿蒙端的一键登录服务。

修复:在 DCloud 后台为鸿蒙包名新建应用,开通 uni 一键登录服务。

4.3 第三个坑:关闭弹窗 API 不同

Android/iOS 用 uni.closeAuthView(),鸿蒙端必须改用 manager.close()。

五、前端实现:鸿蒙端专属的一键登录流程

5.1 API 差异一览

// Android/iOS:通过 uni.login 一步完成
uni.login({
  provider: 'univerify',
  univerifyStyle: { /* 丰富的样式配置 */ },
  success: (res) => { /* 获取 access_token */ },
});
uni.closeAuthView();  // 关闭弹窗

// 鸿蒙端:通过 manager 实例分步操作
const manager = uni.getUniverifyManager();
manager.preLogin({ success: () => {} });     // 第一步:预登录
manager.login({                              // 第二步:登录
  univerifyStyle: { fullScreen: false, logoPath: '...', loginBtnText: '...' },
  success: (res) => {},
});
manager.close();  // 关闭弹窗

5.2 鸿蒙端完整实现

// #ifdef APP-HARMONY
let univerifyManager: any = null;

const handleHarmonyUniVerifyLogin = async () => {
  if (typeof (uni as any).getUniverifyManager !== 'function') {
    uni.showToast({ title: '当前设备不支持一键登录', icon: 'none' });
    return;
  }

  univerifyManager = (uni as any).getUniverifyManager();
  const closeAuthView = () => univerifyManager?.close();

  // 授权成功:通过云函数换取手机号
  const handleSuccess = async (res: any) => {
    uni.showLoading({ title: '登录中...', mask: true });
    try {
      const cloudRes = await uniCloud.callFunction({
        name: 'getPhoneNumber',
        data: {
          access_token: res.authResult.access_token,
          openid: res.authResult.openid,
        },
      });
      if (cloudRes?.result?.phoneNumber) {
        await loginByUniverify({ phone: cloudRes.result.phoneNumber, type: 4 });
        await afterAppLogin(resList);
      } else {
        uni.showToast({ title: '未获取到本机号码', icon: 'none' });
      }
    } catch (err) {
      uni.showToast({ title: '获取手机号失败,请使用验证码登录', icon: 'none' });
    } finally {
      uni.hideLoading();
      closeAuthView();
    }
  };

  // 授权失败处理
  const handleFail = (err: any) => {
    uni.hideLoading();
    if (err?.metadata?.resultCode == '102103') {
      uni.showToast({ title: '请先打开移动数据(流量)', icon: 'none' });
    }
    if (err?.code == '30002') {
      toggleLoginType(LoginType.MOBILE);
    }
  };

  uni.showLoading({ title: '加载中...', mask: true });

  const doLogin = () => {
    univerifyManager!.login({
      univerifyStyle: {
        fullScreen: false,
        logoPath: logoSrc.value,
        loginBtnText: '本机号码一键登录',
      },
      success: handleSuccess,
      fail: handleFail,
    });
  };

  // 检查预登录状态
  if (univerifyManager!.isPreLoginValid?.()) {
    doLogin();
  } else {
    univerifyManager!.preLogin({
      success: () => doLogin(),
      fail: (preErr: any) => {
        uni.hideLoading();
        uni.showToast({
          title: preErr?.errMsg ?? '预授权失败,请使用手机号验证码登录',
          icon: 'none',
        });
        setTimeout(() => toggleLoginType(LoginType.MOBILE), 2000);
      },
    });
  }
};
// #endif

5.3 入口方法的条件编译分流

const handleUniVerifyLogin = async () => {
  // #ifdef APP-HARMONY
  await handleHarmonyUniVerifyLogin();
  // #endif

  // #ifndef APP-HARMONY
  // Android/iOS 保持原有逻辑不变
  uni.login({ provider: 'univerify', univerifyStyle: { /* ... */ }, ... });
  // #endif
};

[配图:客户端调用成功后的授权弹窗]

六、预登录优化

利用页面加载时间提前预登录,可以显著提升用户点击后的响应速度:

// 页面 onLoad 时提前预登录
// #ifdef APP-HARMONY
if (typeof (uni as any).getUniverifyManager === 'function') {
  const manager = (uni as any).getUniverifyManager();
  manager.preLogin({
    success: () => console.log('[Harmony] preLogin success'),
    fail: (err) => console.warn('[Harmony] preLogin fail', JSON.stringify(err)),
  });
}
// #endif

七、样式支持对比

鸿蒙端的 univerifyStyle 支持范围远小于 Android/iOS:

配置项 Android/iOS 鸿蒙端
fullScreen ✅ ✅
logoPath ✅ ✅
loginBtnText ✅ ✅
backgroundColor ✅ ❌
phoneNumColor ✅ ❌
authButton.* ✅ ❌
closeButtonStyle ✅ ❌
privacyTerms ✅ ❌
timeout ✅ ❌

八、错误码速查表

errCode 含义 处理方式
4000 缺少参数(appid/gyid) 检查 module.json5 的 GETUI_APPID
- AppId 未开通一键登录 DCloud 后台开通 uni-verify 服务
102103 未开启移动数据 提示用户打开流量
30002 需切换登录方式 自动降级到验证码登录

九、总结

  1. AppID 必须新建:鸿蒙包名与 Android/iOS 不同,需在 DCloud 后台新建应用。
  2. 平台服务要先开通:未开通 uni 一键登录或计费异常,客户端会直接报错。
  3. metadata 是命门:GETUI_APPID 必须配置在 module.json5 的 metadata 中。
  4. API 完全不同:鸿蒙端用 getUniverifyManager() 替代 uni.login(),用 manager.close() 替代 uni.closeAuthView()。
  5. 样式大幅缩水:鸿蒙端只支持 fullScreen、logoPath、loginBtnText 三个样式属性。
  6. 预登录是加分项:利用页面加载时间提前预登录,显著提升响应速度。
  7. 四处 AppID 必须一致:DCloud 后台、manifest.json、module.json5、云函数必须使用同一个鸿蒙端 AppID。

posted on 2026-09-24 12:28  码间留白  阅读(6)  评论(0)    收藏  举报

导航