HarmonyOSNext模块化设计实践:打造简洁高效的登录注册页面

涉及知识点和装饰器

  • @ComponentV2,@Local, @Builder,@Extend, @Event,!!双向绑定,@Require ,@Param,preferences首选项

先上效果图



完整代码

登录页

import { Route } from '@hzw/zrouter'
import { CommonConst, NavName } from 'utils'
import { ToastUtil } from '@pura/harmony-utils'
import { SwitchLoginTypeComp } from '../components/SwitchLoginTypeComp'
import { LoginService } from '../service/LoginService'
import json from '@ohos.util.json'

@Preview
@ComponentV2
@Route({ name: NavName.LOGIN_VIEW, description: '登录/注册页' })
export struct LoginView {
  @Local account: string = '' //  用户名
  @Local password: string = '' // 密码
  @Local isLogin: boolean = true // 登录注册切换



  aboutToAppear(): void {
    // console.log(`xxx : ---` + 'LoginView')
    // console.log(`xxx获取参数 : ---` + ZRouter.getInstance().getParamByName(NavName.LOGIN_VIEW).toString())

  }

  build() {
    NavDestination() {
      Column() {
        this.headBuilder()
       // 登录,注册页面
        SwitchLoginTypeComp({
          changeFactory: (account: string, password: string, isLogin: boolean) => {
            console.log(`xxx : ---account--` + account + ',password--' + password + ',isLogin--' + isLogin)
            this.account = account
            this.password = password
            this.loadNet()
          }
        })


      }.width(CommonConst.FULL_PARENT)
      .height(CommonConst.FULL_PARENT)
    }
    .hideTitleBar(true)
    .backgroundColor($r('app.color.white'))
    .width(CommonConst.FULL_PARENT)
    .height(CommonConst.FULL_PARENT)

  }

  async loadNet() {
    let login = await LoginService.login({
      'username': this.account,
      'password': this.password
    })
    console.log('xxxlogin--' + '' + json.stringify(login))
    if (login.data?.getSuccess()) {

      console.log('xxxxxdata参数--' + '' + json.stringify(login.data.getData()))
    } else {
      console.log('xxx--' + login.data?.getMsg())
      ToastUtil.showToast(login.data?.getMsg())
    }
  }

  /** 头部 */
  @Builder
  headBuilder() {
    Stack({ alignContent: Alignment.Top }) {
      Text()
        .width(CommonConst.FULL_PARENT)
        .height(250)
        .backgroundColor($r('app.color.colorPrimary'))
      Text()
        .width(CommonConst.FULL_PARENT)
        .height(100)
        .backgroundColor(Color.White)
        .borderRadius(50)
        .margin({ top: 210 })

      Image($r('app.media.ic_close'))
        .width(20)
        .height(20)
        .position({ x: 20, y: 20 })
        .onClick(() => {
          ToastUtil.showToast('关闭')
        })

      Column({ space: 7 }) {
        Image($r('app.media.startIcon')).width(100).height(100)
        Text('欢迎使用').fontSize(22).fontColor($r('app.color.white')).fontWeight(FontWeight.Bold)
        Text('本APP由JasonYin独立开发').fontSize(12).fontColor($r('app.color.white'))
      }.margin({ top: 44 })
    }.width(CommonConst.FULL_PARENT)

  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

登录/注册页面自定义组件

import { ToastUtil } from '@pura/harmony-utils'
import { CommonConst } from "utils"
import { ProtocolComp } from "./ProtocolComp"

@Extend(TextInput)
function inputStyle() {
  .height(44)
  .fontColor($r('app.color.color_222222'))
  .fontSize(16)
  .placeholderColor($r('app.color.color_999999'))
  .placeholderFont({ size: 16 })
  .backgroundColor($r('app.color.white'))
  .caretColor($r('app.color.colorPrimary'))
  .padding({ left: 8 })
}

@Extend(Divider)
function dividerStyle() {
  .strokeWidth(1)
  .color($r('app.color.color_F0F0F0'))
  .padding({ left: 60, right: 60 })

}

/**
 * Author:J
 * Describe: 登录,注册页面
 */
@ComponentV2
export struct SwitchLoginTypeComp {
  @Local account: string = '鸿蒙屌爆了' //  用户名
  @Local password: string = '123456' // 密码
  @Local confirmPassword: string = '' // 确认密码
  @Local isLogin: boolean = true // 登录注册切换
  @Local selectProtocol: boolean = false; //是否勾选协议
  @Local confirm: boolean = false; //是否弹窗协议
  @Event changeFactory: (account: string, password: string, isLogin: boolean) => void = () => {
  }

  build() {
    Column() {
      Text() {
        ImageSpan($r('app.media.ic_next'))
          .width(15)
          .colorBlend($r('app.color.colorPrimary'))
          .rotate({ angle: 180 })
          .margin({ right: 8, bottom: 2 })
          .visibility(this.isLogin ? Visibility.Hidden : Visibility.Visible)
        Span(this.isLogin ? '去注册' : '去登录')
        ImageSpan($r('app.media.ic_next'))
          .width(15).colorBlend($r('app.color.colorPrimary'))
          .margin({ left: 8, bottom: 2 })
          .visibility(this.isLogin ? Visibility.Visible : Visibility.Hidden)
      }
      .fontSize(15)
      .fontColor($r('app.color.colorPrimary'))
      .onClick(() => {
        this.isLogin = !this.isLogin
        this.account = ''
        this.password = ''
        this.confirmPassword = ''
      })

      Row() {
        Image($r('app.media.ic_account_normal')).width(24).height(24)
        TextInput({ placeholder: '请输入用户名', text: `${this.account}` })
          .inputStyle()
          .onChange((value: string) => {
            this.account = value
          })
        Image($r('app.media.ic_delete')).width(14).height(14)
          .visibility(this.account.length > 0 ? Visibility.Visible : Visibility.None)
          .onClick(() => {
            this.account = ''
          })
      }.width(CommonConst.FULL_PARENT).height(44).padding({ left: 60, right: 100 }).margin({ top: 40 })

      Divider().dividerStyle()

      Row() {
        Image($r('app.media.ic_password_normal')).width(24).height(24)
        TextInput({ placeholder: '请输入密码', text: `${this.password}` })
          .inputStyle()
          .type(InputType.Password)
          .onChange((value: string) => {
            this.password = value
          })
      }.width(CommonConst.FULL_PARENT).height(44).padding({ left: 60, right: 60 })

      Divider().dividerStyle()

      Row() {
        Image($r('app.media.ic_password_normal')).width(24).height(24)
        TextInput({ placeholder: '请确认密码', text: `${this.confirmPassword}` })
          .inputStyle()
          .type(InputType.Password)
          .onChange((value: string) => {
            this.confirmPassword = value
          })
      }
      .width(CommonConst.FULL_PARENT)
      .height(44)
      .padding({ left: 60, right: 60 })
      .visibility(this.isLogin ? Visibility.None : Visibility.Visible)

      Divider().dividerStyle()
      // 协议相关
      ProtocolComp({ selectProtocol: this.selectProtocol!!, confirm: this.confirm!! })


      Button(this.isLogin ? '登录' : '注册')
        .width('80%')
        .height(44)
        .fontSize(15)
        .fontColor($r('app.color.white'))
        .backgroundColor($r('app.color.colorPrimary'))
        .margin({ top: 31 })
        .enabled(this.account.length > 0 && this.password.length > 0)
        .onClick(() => {
          if (!this.isLogin && this.password != this.confirmPassword) {
            ToastUtil.showToast('两次密码不一致')
            return
          }
          if (!this.selectProtocol) {
            // ToastUtil.showToast('' + '请勾选协议')
            this.confirm = !this.selectProtocol
            return
          }
          this.changeFactory(this.account, this.password, this.isLogin)
        })


    }
    .width(CommonConst.FULL_PARENT)
    .margin({ top: -80 })

  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139

用户协议、隐私政策相关

import { ToastUtil } from '@pura/harmony-utils'
import { CommonConst } from "utils";

/**
 * Author:J
 * Describe:协议
 */
@ComponentV2
export struct ProtocolComp {
  @Require @Param selectProtocol: boolean=false //是否勾选协议
  @Event $selectProtocol: (val: boolean) => void = () => {
  }
  @Require @Param confirm: boolean=false //是否弹窗协议
  @Event $confirm: (val: boolean) => void = () => {
  }

  build() {
    Column() {
      Row() {
        Checkbox()
          .select(this.selectProtocol)
          .selectedColor($r('app.color.colorPrimary'))
          .onChange((value: boolean) => {
            this.$selectProtocol(value)
            //协议不选中,不赋值confirm
            if (!value) {
              return
            }
            this.$confirm(!value)
          })
        Text() {
          span('登录即表示您已同意我们的 ', $r('app.color.color_999999'))
          span('《隐私政策》', $r('app.color.colorPrimary'), 1, true)
          span('、', $r('app.color.color_999999'))
          span('《用户服务协议》', $r('app.color.colorPrimary'), 2, true)
          span('、', $r('app.color.color_999999'))
          span('《服务条款》', $r('app.color.colorPrimary'), 3, true)
        }
      }
      .justifyContent(FlexAlign.Start)
      .alignItems(VerticalAlign.Top)
      .margin({ top: 14, right: 30 })

      // 若没有勾选协议,则会出现提示文本:“请阅读并勾选协议”
      if (this.confirm) {
        Row() {
          Column() {
            Image($r('app.media.login_popup'))
              .width(10)
              .height(10)
              .position({ x: 7, y: 1 })
              .opacity(0.8)

            Text('请阅读并勾选协议')
              .fontColor($r('app.color.white'))
              .fontSize(10)
              .textAlign(TextAlign.Center)
              .backgroundColor($r('app.color.black'))
              .width(100)
              .height(20)
              .position({ x: 3, y: 9 })
              .borderRadius(4)
              .opacity(0.8)
          }
          .width(100)
          .height(30)
        }
        .height(30)
        .width('100%')
      }


    }.width(CommonConst.FULL_PARENT)
    .height(50)
    .padding({ left: 60, right: 60 })

  }
}


@Builder
function span(value: string | Resource, fontColor: ResourceColor, k: number = 0, isClick: boolean = false) {
  Span(value)
    .fontColor(fontColor)
    .fontSize(12)
    .onClick(() => {
      if (!isClick) {
        return
      }
      ToastUtil.showToast(`我要进web页面了k:${value}=====值:${k}`)
    })
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

使用首选项存储用户信息到本地

// 存
 PreferencesUtil.put(PreferencesConst.KEY_LOGIN_USER_BEAN, JSONUtil.beanToJsonStr(login.data?.getData()))
 // 取
let user = await PreferencesUtil.getString(PreferencesConst.KEY_LOGIN_USER_BEAN)
let userBean = JSONUtil.jsonToBean(UserLoginBean, user)
  • 1
  • 2
  • 3
  • 4
  • 5

以往系列文章

  1. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 模块化基础篇》
  2. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 构建基础特性层》
  3. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 构建公共能力层》
  4. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— Tabs底部导航栏》
  5. 《探索 HarmonyOS NEXT (5.0):开启构建模块化项目架构奇幻之旅 —— 动态路由 ZRouter:引领高效模块通信的智慧中枢》
  6. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 ——第三方库的使用:网络请求RCP、二次封装上下拉刷新、弹窗》
  7. HarmonyOS NEXT:模块化项目 ——修改应用图标+启动页等

若本文对您稍有帮助,诚望您不吝点赞,多谢。

有兴趣的同学可以点击查看源码

欢迎加我微信一起交流:+V:yinshiyuba

本文使用 文章同步助手 同步

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容