#我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程! 原创 精华

peterpan527
发布于 2025-5-8 23:40
浏览
1收藏

客户端介绍

在鸿蒙开发的广阔天地里,工具的选择至关重要。今天,就来跟大家分享我使用 Atomgit 客户端进行鸿蒙开发的实战经历,说不定能为你的鸿蒙开发之路点亮一盏明灯!

项目概述和创新点

本项目是一个功能全面的社区客户端,采用axios网络库实现数据请求。它不仅支持用户首次使用的授权说明,还提供用户注册、登录功能,并包含隐私协议和用户协议的详细说明。客户端界面分为首页和我的信息页两大板块。

首页设计精心,集成了轮播广告、精彩活动、资讯展示等多个模块,同时设有关于页面,为用户提供全面的信息浏览体验。首页的tab页签布局清晰,分为推荐、最新、仓库、组织等核心功能区,方便用户快速定位感兴趣的内容。个人信息页面则专注于用户账户管理,支持注册、登录以及隐私协议和用户协议的授权。

应用功能说明

用户授权与账户管理

  • 首次使用授权说明:为用户提供初次使用时必须了解的授权信息。
    #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区- #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区

首页

  • 首页轮播广告获取:通过POST请求获取并展示社区的最新活动或重要通知,支持点击跳转到详情页。
  • 首页推荐获取:通过POST请求获取推荐资讯的数据,点击跳转到详情页。
  • 首页最新获取:通过POST请求获取展示社区中最新发布的内容,点击跳转到详情页。
  • 首页仓库获取:通过POST请求获取仓库资讯的数据,点击跳转到详情页。
  • 首页组织获取:通过POST请求获取展示社区中的组织结构,包括用户所在的团队或感兴趣的小组,点击跳转到详情页。
    #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区
    #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区

我的页面

  • **个人信息页面
  • **用户注册:允许新用户创建账户并加入社区。
    #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区
  • **用户登录:允许已注册用户登录以访问个人账户。
    #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区
    #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区
  • 关于 提供社区的背景信息和联系方式,支持点击跳转到详情页。
    #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区
  • **用户协议隐私授权:用户在此页面授权同意隐私协议,确保账户安全和数据保护。
    #我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程!-鸿蒙开发者社区

技术栈概览

  • ArkTS:作为本项目开发的核心编程语言,ArkTS以其卓越的性能和简洁的语法,为项目提供了稳定而高效的代码实现。
  • ArkUI:选用ArkUI作为界面开发的UI框架,它以其直观的设计和响应式布局,确保了用户界面的美观和易用性,提升了用户体验。
  • Axios:在处理HTTP请求方面,我们采用了Axios库,它以其简洁的API和强大的功能,简化了网络请求的处理,提高了数据交互的效率。
  • 通过这些精选的技术工具,我们的项目团队能够构建一个既强大又用户友好的社区客户端,为用户提供流畅且直观的交互体验

商业价值分析

本项目旨在打造一个资讯类客户端平台,通过精心设计的展示功能,促进信息的流通和社区成员之间的交流。以下是项目的核心价值和功能亮点:

信息流通与社区互动

  • 最新广告展示:客户端将展示最新的广告内容,让用户及时了解社区内外的最新动态和商业信息。
  • 精彩活动推荐:精选社区内外的精彩活动,鼓励用户参与,增加社区的活跃度和凝聚力。
  • 推荐内容:根据用户兴趣和社区热度推荐相关内容,提升用户体验和内容的个性化。
  • 仓库与组织列表:展示社区内的资源库和组织结构,方便用户查找和利用社区资源,同时也增强了社区的组织性和结构性。

增强社区凝聚力

  • 通过提供丰富的资讯和活动,本项目能够吸引用户积极参与社区生活,从而增强社区成员之间的联系和社区的整体凝聚力。

广告主和组织的价值平台

  • 展示平台:为广告主和组织提供了一个展示自己工作成果和成就的平台,增加曝光度和影响力。
  • 互动交流:通过客户端的互动功能,广告主和组织可以直接与用户沟通,收集反馈,优化服务。

项目目标

本项目通过提供一个集资讯、广告、活动和社区资源于一体的客户端平台,旨在构建一个信息流通顺畅、社区活跃度高、凝聚力强的在线社区环境。同时,它也为广告主和组织提供了一个有效的宣传和交流渠道,实现了社区与商业的双赢。

环境配置

环境配置说明

  • 系统要求

    • Windows环境
      • 操作系统:Windows10 64位、Windows11 64位
      • 内存:16GB及以上
      • 硬盘:100GB及以上
      • 分辨率:1280*800像素及以上
    • macOS环境
      • 操作系统:macOS(X86) 11/12/13/14 macOS(ARM) 12/13/14
      • 内存:8GB及以上
      • 硬盘:100GB及以上
      • 分辨率:1280*800像素及以上
  • 依赖管理:项目依赖于DevEco Studio开发工具与三方库的Axios。依赖管理通过DevEco Studio内置的项目管理工具进行,确保版本一致性和依赖的自动更新。

编译构建

  • 构建流程:项目构建流程包括代码编译、资源打包、应用签名和部署测试。
  • 依赖安装:用户可以通过DevEco Studio的项目管理工具一键安装所有依赖,或手动通过命令行工具进行安装。

核心代码

登录页面

import { promptAction, router } from '@kit.ArkUI';
import { http } from '@kit.NetworkKit';
import CommonConstants from '../../utils/CommonConstants';
import Logger from '../../utils/Logger';

import { util } from '@kit.ArkTS';
import HelperUtil from '../../utils/HelperUtil';
import { CommonDialog } from '../../utils/CommonDialog';
import { LoginParams, LoginResult } from '../Model/UserModel';
import { GlobalContext } from '../../utils/GlobalContext';
import { preferences } from '@kit.ArkData';
import { HdNav } from '../../common/HdNav';

interface ParamsType {
  label: string
}


@Entry
@Component
struct Login {
  @State message: string = '登录帐号以使用更多服务';
  @State isAcceptProtocol: boolean = false;
  @State isCodeLoginShow: boolean = false
  @State addressLabel: string = '中国 +86'
  @State dialogTitle: Resource = $r('app.string.dialog_about_title')
  @State dialogContent: Resource = $r('app.string.project_desc')
  dialogController: CustomDialogController = new CustomDialogController({
    builder: CommonDialog({ title: $dialogTitle, description: $dialogContent, showPositive: false }),
    alignment: DialogAlignment.Center
  })
  title: string = '登录';
  @State username: string = "";
  @State par: LoginParams = {
  } as LoginParams;

  /**
   * 获取文章列表
   * @param par
   * @returns
   */
  async dataRequest(par: LoginParams): Promise<void> {
    console.log(" 用户名 " + par.username);

    if (HelperUtil.isEmpty(par.username)) {
      HelperUtil.showToast("请输入登录用户名!");
      return;
    }
    if (HelperUtil.isEmpty(par.password)) {
      HelperUtil.showToast("请输入登录用户密码!");
      return;
    }
    HelperUtil.digest(new util.TextEncoder().encodeInto(par.password));
    let username = encodeURI(par.username);
    let pwd = await HelperUtil.md5(par.password);


    let url ='https://openatom.atomgit.com/api/user/isMembers';
    console.log(" 用户名 " + url)
    let httpRequest = http.createHttp();
    httpRequest.request(
      url
    ).then((res) => {
      Logger.info(CommonConstants.HOME_PAGE_TAG, '请求结果: ' + res.result as string)
      let rt = JSON.parse(res.result as string) as LoginResult; //total
      let message = "";
      switch (rt.code.toString()) {
        case "200":
          message = "登录成功";
          //需要存储用户的uid,username,等信息到本地,
          this.saveLogin(rt);
          router.pushUrl({
            url: 'pages/Index',
            params: rt,
          })
          HelperUtil.showToast(message);
          return;
        case "θ":
          message = "服务器错误";
          break;
        case "00":
          message = "内部处理错误";
          break;
        case "000":
          message = "用户名/密码未填写";
          break;
        case "102":
          message = "用户名不存在";
          break;
        case "103":
          message = "密码不正确";
          break;
        case "101":
          message = "登录成功";
          break;
          case "400200":
            message = "非法社区";
            break;
        default:
          message = "登录失败";
      }
      HelperUtil.showToast(rt.code + " " + message);
    })
      .catch((err: Error) => {
        HelperUtil.showToast("请求服务异常:" + err.message)
        Logger.error(CommonConstants.HOME_PAGE_TAG, JSON.stringify(err));
      })
      .finally(() => {
        httpRequest.destroy()
      })
  }

  onPageShow(): void {
    if (router.getParams() !== undefined) {
      let params = router.getParams() as Record<string, string>;
      this.username = params["username"] as string;
      this.par.username = this.username;
    }
  }

  build() {
    Column() {
      HdNav()
      Image($r('app.media.startIcon'))
        .width(50)
        .margin({ top: 15 })
      Text(this.title)
        .fontSize(25)
        .fontWeight(900)
        .margin({ top: 20, bottom: 5 })
      Text(this.message)
        .fontSize(14)
        .fontColor('#666')
        .margin({ bottom: 10 })

      // 短信验证码登录
      if (this.isCodeLoginShow) Column() {
        Row() {
          Text('国家/地区')
            .fontColor('#666')
          Row() {
            Text(this.addressLabel)
              .fontColor('#666')
            Image($r('app.media.ic_public_arrow_right'))
              .width(20)
              .fillColor('#666')
          }
          .onClick(() => {
            router.pushUrl({
              url: 'pages/CountryRegion',
            })
          })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        .padding({ left: 10, right: 10 })
        .margin({ bottom: 20 })

        TextInput({
          placeholder: '手机号'
        })
          .maxLength(20)
          .type(InputType.PhoneNumber)
        Row({ space: 10 }) {
          TextInput({
            placeholder: '短信验证码'
          })
            .width('60%')
            .type(InputType.Number)
          Button('获取验证码')
            .buttonStyle(ButtonStyleMode.NORMAL)
            .onClick(() => {
            })
        }
        .margin({ top: 20, bottom: 30 })
        .width('100%')

        Button('登录')
          .width('100%')
          .onClick(() => {
          })
          .margin({ bottom: 15 })
        Text('密码登录')
          .fontColor('#007dff')
          .onClick(() => {
            this.isCodeLoginShow = false
          })
      }
      // 密码登录
      if (!this.isCodeLoginShow) Column() {
        TextInput({
          text: this.username, //"",
          placeholder: '登录账号'
        })
          .maxLength(15)
          .type(InputType.USER_NAME)
          .onChange((value) => {
            this.par.username = value;
          })
        TextInput({
          text: "", //"1234567",
          placeholder: '密码'
        })
          .type(InputType.Password)
          .margin({ top: 20, bottom: 30 })
          .onChange((value) => {
            this.par.password = value;
          })

        Row() {
          Radio({
            group: "",
            value: '同意'
          }).onClick(() => {
            this.isAcceptProtocol = !this.isAcceptProtocol;
          }).checked(this.isAcceptProtocol);
          Text("我已阅读并同意").fontSize(14)
          Text('用户协议').fontColor('#007dff').fontSize(14)
            .onClick(() => {
              let u =
                getContext(this).resourceManager.getStringValue($r("app.string.terms_use_url").id, (error, value) => {
                  if (error != null) {
                    console.log("error is " + error);
                  } else {
                    this.openWebUrl(value);
                    console.log("openWebUrl is " + value);
                  }
                });
            })
          Text("和").fontSize(14)
          Text('隐私声明').fontColor('#007dff').fontSize(14)
            .onClick(() => {
              let u =
                getContext(this)
                  .resourceManager
                  .getStringValue($r("app.string.privacy_agreement_url").id, (error, value) => {
                    if (error != null) {
                      console.log("error is " + error);
                    } else {
                      this.openWebUrl(value);
                      console.log("openWebUrl is " + value);
                    }
                  });
            })
        }.margin({ bottom: 20 })

        Button('登录')
          .width('100%')
          .onClick(() => {
            if (!this.isAcceptProtocol) {
              HelperUtil.showToast("请先确认用户协议!");
              return;
            }
            this.dataRequest(this.par);
          })
          .margin({ bottom: 15 })
        Button('注册账号')
          .width('100%')
          .buttonStyle(ButtonStyleMode.NORMAL)
          .onClick(() => {
            router.pushUrl({
              url: 'pages/User/Register',
            })
          })
          .margin({ bottom: 15 })
      }

      Blank()
      Column() {
        Text('其他方式登录')
          .fontColor('#666')
          .fontSize(14)
        Image($r('app.media.ic_public_comments'))
          .width(40)
          .border({
            width: 1,
            color: '#ccc'
          })
          .padding(10)
          .borderRadius(20)
          .margin({ top: 20, bottom: 20 })
      }.visibility(Visibility.Hidden)

      Row() {
        Text('遇到问题').fontColor('#007dff').fontSize(14)
          .onClick(() => {
            this.dialogController.open();
          })
        Divider()
          .vertical(true)
          .height(20)
          .width(2)
          .color('#666')
          .margin({ left: 20, right: 20 })
        Text('用户协议').fontColor('#007dff').fontSize(14)
          .onClick(() => {
            let u =
              getContext(this).resourceManager.getStringValue($r("app.string.terms_use_url").id, (error, value) => {
                if (error != null) {
                  console.log("error is " + error);
                } else {
                  this.openWebUrl(value, "用户协议");
                  console.log("openWebUrl is " + value);
                }
              });
          })
        Divider()
          .vertical(true)
          .height(20)
          .width(2)
          .color('#666')
          .margin({ left: 20, right: 20 })
        Text('隐私声明').fontColor('#007dff').fontSize(14)
          .onClick(() => {
            let u =
              getContext(this)
                .resourceManager
                .getStringValue($r("app.string.privacy_agreement_url").id, (error, value) => {
                  if (error != null) {
                    console.log("error is " + error);
                  } else {
                    this.openWebUrl(value, "隐私声明");
                    console.log("openWebUrl is " + value);
                  }
                });
          })
      }
    }
    .width("100%")
    .height('100%')
    .padding(20)
  }

  /**
   * Get data preferences action.
   */
  getDataPreferences(common: Object) {
    return preferences.getPreferences(getContext(common), CommonConstants.PREFERENCES_FILE_NAME);
  }

  saveLogin(user: LoginResult) {

    GlobalContext.getContext().isLogin = true;
    GlobalContext.getContext().userId = user.uid;
    GlobalContext.getContext().setObject('user', user);

    let preferences: Promise<preferences.Preferences> = this.getDataPreferences(this);
    preferences.then((result: preferences.Preferences) => {
      let privacyPut = result.put(CommonConstants.LOGIN_USER_KEY, user);
      result.flush();
      privacyPut.then(() => {
        Logger.info(CommonConstants.LOGIN_PAGE_TAG, 'Put the value of startup Successfully.');
      }).catch((err: Error) => {
        Logger.error(CommonConstants.LOGIN_PAGE_TAG, 'Put the value of startup Failed, err: ' + err);
      });
    }).catch((err: Error) => {
      Logger.error(CommonConstants.LOGIN_PAGE_TAG, 'Get the preferences Failed, err: ' + err);
    });
  }

  openWebUrl(url: string, title?: string) {
    router.pushUrl({
      url: 'pages/WebViewPage',
      params: {
        title: title,
        url: url  // 传递的 URL 参数
      }
    }, router.RouterMode.Single)
  }
}

注册页面

import { promptAction, router } from '@kit.ArkUI';
import { http } from '@kit.NetworkKit';
import CommonConstants from '../../utils/CommonConstants';
import Logger from '../../utils/Logger';

import { util } from '@kit.ArkTS';
import HelperUtil from '../../utils/HelperUtil';
import { CommonDialog } from '../../utils/CommonDialog';
import { CustomResult, LoginResult, RegisterParams, RegisterResult } from '../Model/UserModel';
import { HdNav } from '../../common/HdNav';
import { systemDateTime } from '@kit.BasicServicesKit';
import { preferences } from '@kit.ArkData';
import { GlobalContext } from '../../utils/GlobalContext';
import RegExpUtils from '../../utils/RegExpUtils';

interface ParamsType {
  label: string
}


@Entry
@Component
struct Register {
  @State countCheckCodeSeconds: number = 60;
  private timeId: number = 0;
  @State isCodeLoginShow: boolean = false
  @State addressLabel: string = '中国 +86'
  title: string = '注册';
  @State isAcceptProtocol: boolean = false;
  @State getCheckCode: string = '获取验证码';
  @State isNext: boolean = false;
  @State par: RegisterParams = {
  } as RegisterParams;

  onPageShow(): void {
    if (router.getParams() !== undefined) {
      this.addressLabel = (router.getParams() as ParamsType).label
    }
  }

  build() {
    Column() {
      HdNav({
        title: this.title
      })
      Text($r("app.string.app_name")).fontSize(28).fontWeight(FontWeight.Bold).margin({ top: 15, bottom: 15 })
      if (!this.isNext) {
        // 短信验证码登录
        TextInput({
          placeholder: '手机号'
        })
          .onChange((value) => {
            //手机号
            this.par.mobile = value;
          })
          .maxLength(11)
          .type(InputType.PhoneNumber)
        Row({ space: 10 }) {
          TextInput({
            placeholder: '短信验证码'
          })
            .onChange((value) => {
              this.par.code = value;
            })
            .width('60%')
            .type(InputType.Number)
          Button(this.getCheckCode)
            .buttonStyle(ButtonStyleMode.NORMAL)
            .onClick(() => {
              if (!RegExpUtils.checkPhone(this.par.mobile)) {
                return
              }
              //验证是否注册过
              this.isRegisterRequest(this.par.mobile);
            })
            .enabled(this.getCheckCode == "获取验证码")
        }
        .margin({ top: 20, bottom: 20 })
        .width('100%')

        Row() {
          Radio({
            group: "",
            value: '同意'
          }).onClick(() => {
            this.isAcceptProtocol = !this.isAcceptProtocol;
          }).checked(this.isAcceptProtocol);
          Text("我已阅读并同意").fontSize(14)
          Text('用户协议').fontColor('#007dff').fontSize(14)
            .onClick(() => {
              let u =
                getContext(this).resourceManager.getStringValue($r("app.string.terms_use_url").id, (error, value) => {
                  if (error != null) {
                    console.log("error is " + error);
                  } else {
                    this.openWebUrl(value);
                    console.log("openWebUrl is " + value);
                  }
                });
            })
          Text("和").fontSize(14)
          Text('隐私声明').fontColor('#007dff').fontSize(14)
            .onClick(() => {
              let u =
                getContext(this)
                  .resourceManager
                  .getStringValue($r("app.string.privacy_agreement_url").id, (error, value) => {
                    if (error != null) {
                      console.log("error is " + error);
                    } else {
                      this.openWebUrl(value);
                      console.log("openWebUrl is " + value);
                    }
                  });
            })
        }.margin({ bottom: 20 })

        Button('下一步')
          .width('100%')
          .onClick(() => {
            this.checkmobileCodeRequest(this.par);
          })
          .margin({ bottom: 15 })
          .enabled(this.isAcceptProtocol && this.par.code?.length == 6 && this.par.mobile?.length == 11)
        Blank()

      } else { //next
        TextInput({
          text: "",
          placeholder: '请填写登录账号,长度3~15字符'
        })
          .maxLength(15)
          .type(InputType.USER_NAME)
          .onChange((value) => {
            this.par.username = value;
          })
        TextInput({
          placeholder: '请填写密码,长度6~15字符'
        })
          .maxLength(15)
          .type(InputType.Password)
          .margin({ top: 20, bottom: 30 })
          .onChange((value) => {
            this.par.password = value;
          })
        Text("填写用户名和密码即可完成注册")
          .fontSize(16)
          .margin({ bottom: 10 })
        Text("注意:初始密码默认为手机号后 6 位,也可在当前页面进行设定")
          .fontColor(Color.Red)
          .fontSize(14)
          .margin({ top: 10, bottom: 30 })

        Button('提交')
          .width('100%')
          .onClick(() => {
            //注册
            this.registerRequest(this.par);
          })
          .margin({ bottom: 15 })
          .enabled(this.par.username?.length >= 3 && this.par.password?.length >= 6)
        Blank()

      }
    }
    .width("100%")
    .height('100%')
    .padding(20)
  }

  /**
   * 是否已注册
   * @param userphone
   * @returns
   */
  isRegisterRequest(userphone: string) {
    let url = '';
    let httpRequest = http.createHttp();
    httpRequest.request(
      url
    ).then((res) => {
      Logger.info(CommonConstants.REGISTER_TAG, '请求结果: ' + res.result as string)

      let rt = JSON.parse(res.result as string) as CustomResult; //total
      let message = "";
      if (rt.result == "-1") {
        HelperUtil.showToast("该手机号已注册,请换一个手机号!")
      } else {
        this.mobileCodeRequest(this.par.mobile);
        //请求验证码
        this.getCheckCode = this.countCheckCodeSeconds.toString() + "s后重发";
        this.timeId = setInterval(() => {
          if (this.countCheckCodeSeconds == 0) {
            this.getCheckCode = "获取验证码";
            //可操作
          } else {
            this.countCheckCodeSeconds--;
            this.getCheckCode = this.countCheckCodeSeconds.toString() + "s后重发";
          }
        }, CommonConstants.ADVERTISING_INTERVAL_TIME);
      }
    }).catch((err: Error) => {
      HelperUtil.showToast("请求服务异常:" + err.message)
      Logger.error(CommonConstants.REGISTER_TAG, JSON.stringify(err));
    })
      .finally(() => {
        httpRequest.destroy()
      });
  }

  async mobileCodeRequest(userphone: string) {
    let timeSplit = systemDateTime.getTime().toString().substring(0, 7);
    let sign = await HelperUtil.md5(timeSplit + "mudeguo" + userphone);
    let url =
      ``;
    console.log("验证码: " + url);

    let httpRequest = http.createHttp();
    httpRequest.request(
      url
    ).then((res) => {
      Logger.info(CommonConstants.REGISTER_TAG, '请求结果: ' + res.result as string)
      let rt = JSON.parse(res.result as string) as CustomResult; //total
      if (rt.result == "1") {
        HelperUtil.showToast('手机号验证码已发送');
        this.par.identifier = rt.identifier;
      } else {
        HelperUtil.showToast('获取验证码失败 ' + rt.message)
        this.par.identifier = "";
      }
    }).catch((err: Error) => {
      HelperUtil.showToast("请求服务异常:" + err.message)
      Logger.error(CommonConstants.REGISTER_TAG, JSON.stringify(err));
    })
      .finally(() => {
        httpRequest.destroy()
      })
    return false;
  }

  /**
   * 校验验证码
   * @param par
   * @returns
   */
  async checkmobileCodeRequest(par: RegisterParams) {
    let url =
      ``;
    console.log("验证码: " + url);

    let httpRequest = http.createHttp();
    httpRequest.request(
      url
    ).then((res) => {
      Logger.info(CommonConstants.REGISTER_TAG, '请求结果: ' + res.result as string)
      let rt = JSON.parse(res.result as string) as CustomResult; //total
      if (rt.result == "1") {
        //随机生成用户名和密码
        this.par.username =
          this.par.mobile; //"iyy" + (Math.floor(Math.random() * 9100) + 1000) + this.par.mobile.slice(-4);
        //截取密码
        this.par.password = this.par.mobile.slice(-6);
        this.isNext = true;
      } else {
        HelperUtil.showToast('验证码校验失败 ' + rt.message)
      }
    }).catch((err: Error) => {
      HelperUtil.showToast("请求服务异常:" + err.message)
      Logger.error(CommonConstants.REGISTER_TAG, JSON.stringify(err));
    })
      .finally(() => {
        httpRequest.destroy()
      })
    return false;
  }

  /**
   * Get data preferences action.
   */
  getDataPreferences(common: Object) {
    return preferences.getPreferences(getContext(common), CommonConstants.PREFERENCES_FILE_NAME);
  }

  saveLogin(user: LoginResult) {

    GlobalContext.getContext().isLogin = true;
    GlobalContext.getContext().userId = user.uid;
    GlobalContext.getContext().setObject('user', user);

    let preferences: Promise<preferences.Preferences> = this.getDataPreferences(this);
    preferences.then((result: preferences.Preferences) => {
      let privacyPut = result.put(CommonConstants.LOGIN_USER_KEY, user);
      result.flush();
      privacyPut.then(() => {
        Logger.info(CommonConstants.LOGIN_PAGE_TAG, 'Put the value of startup Successfully.');
      }).catch((err: Error) => {
        Logger.error(CommonConstants.LOGIN_PAGE_TAG, 'Put the value of startup Failed, err: ' + err);
      });
    }).catch((err: Error) => {
      Logger.error(CommonConstants.LOGIN_PAGE_TAG, 'Get the preferences Failed, err: ' + err);
    });
  }

  /**
   * 注册
   * @param par
   * @returns
   */
  async registerRequest(par: RegisterParams): Promise<void> {
    console.log(" 用户名 " + par.username);

    if (HelperUtil.isEmpty(par.username)) {
      HelperUtil.showToast("请输入登录用户名!");
      return;
    }
    if (HelperUtil.isEmpty(par.password)) {
      HelperUtil.showToast("请输入登录用户密码!");
      return;
    }

    HelperUtil.digest(new util.TextEncoder().encodeInto(par.password));
    let username = encodeURI(par.username);
    let pwd = await HelperUtil.md5(par.password);
    let url ='';
    console.log("注册地址: " + url)
    let httpRequest = http.createHttp();
    httpRequest.request(
      url
    ).then((res) => {
      Logger.info(CommonConstants.REGISTER_TAG, '请求结果: ' + res.result as string)
      let rt = JSON.parse(res.result as string) as LoginResult; //RegisterResult; //total
      let message = "";
      switch (rt.result) {
        case "111":
          message = "注册成功";
          this.saveLogin(rt);
          HelperUtil.showToast(message);
          //需要存储用户的uid,username,等信息到本地,
          router.pushUrl({
            url: 'pages/User/Login',
            params: {
              username: rt.username,
            }
          }, router.RouterMode.Single)
          return;
        case "θ":
          message = "服务器错误";
          break;
        case "00":
          message = "内部处理错误";
          break;
        case "800":
          message = "用户名/密码未填写";
          break;
        case "104":
          message = "邮箱非法";
          break;
        case "110":
          message = "服务器错误";
          break;
        case "112":
          message = "用户名已被注册,请更换其他用户名";
          break;
        case "113":
          message = "邮箱已被注册";
          break;
        case "114":
          message = "用户名过长/过短,请输入3-15位字符";
          break;
        case "115":
          message = "该手机号已被注册";
          break;
        default:
          message = " 注册失败";
      }
      HelperUtil.showToast(rt.result + message);

    })
      .catch((err: Error) => {
        HelperUtil.showToast("请求服务异常:" + err.message)
        Logger.error(CommonConstants.REGISTER_TAG, JSON.stringify(err));
      })
      .finally(() => {
        httpRequest.destroy()
      })
  }

  openWebUrl(url: string, title?: string) {
    router.pushUrl({
      url: 'pages/WebViewPage',
      params: {
        title: title,
        url: url  // 传递的 URL 参数
      }
    }, router.RouterMode.Single)
  }
}

首页面

import { getActivityList, getAdvertisements, getInfoNews } from '../../common/api/atomApi';
import { ActivityItem, BannerItem, NewsItem } from '../../common/bean/apiTypes';
import { BusinessError } from '@kit.BasicServicesKit';
import { router } from '@kit.ArkUI';
import Tabs0 from "./Tabs0";
import Tabs1 from "./Tabs1";
import Logger from '../../utils/Logger';
import CommonConstants from '../../utils/CommonConstants';

class BasicDataSource<T> implements IDataSource {

  private listeners: DataChangeListener[] = [];
  private originDataArray: T[] = [];

  totalCount(): number {
    return this.originDataArray.length;
  }

  getData(index: number): T {
    return this.originDataArray[index];
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener);
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      this.listeners.slice(pos, 1);
    }
  }

  // 通知LazyForEach组件需要重新重载所有子组件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知LazyForEach组件需要在index对应索引处添加子组件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }
}

class SwiperDataSource<T> extends BasicDataSource<T> {

  private dataArray: T[] = [];

  totalCount(): number {
    return this.dataArray.length;
  }

  getData(index: number): T {
    return this.dataArray[index];
  }

  // 在列表末尾添加数据并通知监听器
  pushData(data: T): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }

  // 重载数据
  reloadData(): void {
    // 不会引起状态变化
    this.dataArray = [];
    // 必须通过DataChangeListener来更新
    this.notifyDataReload();
  }
}

interface StyleItem {
  id:number;
  name: string;
  pic: string;
}


@Component
export default struct Home {
  @Consume pageStack: NavPathStack
  private swiperController: SwiperController = new SwiperController()
  private swiperData: SwiperDataSource<BannerItem> = new SwiperDataSource()

  @State newsList:NewsItem[] = []
  @State currentIndex: number = 0
  @Builder tabBuilder(title: string, index: number) {
    Column() {
      Text(title)
        .fontColor(this.currentIndex === index ? '#0A59F7' : '#E6000000')
        .fontSize(16)
        .fontWeight(this.currentIndex === index ? FontWeight.Normal : FontWeight.Medium)
        .lineHeight(22)
        .margin({ top: 0, bottom: 7 })

      Divider()
        .width(48)
        .strokeWidth(2)
        .color('#0A59F7')
        .opacity(this.currentIndex === index ? 1 : 0)
    }
  }


  // 组件生命周期
  aboutToAppear() {
    Logger.info('Home aboutToAppear');
    getAdvertisements(5,0).then((res) => {
      Logger.debug(CommonConstants.HOME_PAGE_TAG,res.data.code.toString())
      for (const itm of res.data.data) {
        this.swiperData.pushData(itm)
      }
    }).catch((err:BusinessError) => {
      Logger.debug("request","err.code:%d",err.code.toString())
      Logger.debug("request",err.message)
    });

  }

  // 组件生命周期
  aboutToDisappear() {
    Logger.info('Home aboutToDisappear');
  }

  build() {
    Scroll() {
      Column({ space: 0 }) {
        //title
        Text('AtomGit客户端')
          .fontSize(26)
          .fontWeight(FontWeight.Bold)
          .textAlign(TextAlign.Center)
          .width('100%')
          .height(50)
        // 轮播图
        Swiper(this.swiperController) {
          LazyForEach(this.swiperData, (item: BannerItem) => {
            Stack({ alignContent: Alignment.Center }) {
              Image(item.picUrl)
                .width('100%')
                .height(180)
                .backgroundColor(0xAFEEEE)
                .zIndex(1)
                .onClick(() => {
                  router.pushUrl({
                    url: 'pages/WebViewPage',
                    params: { url: item.jumpUrl }
                  });
                })

              // 显示轮播图标题
              Text(item.operateName)
                .padding(5)
                .margin({ top: 135 })
                .width('100%')
                .height(60)
                .textAlign(TextAlign.Center)
                .maxLines(2)
                .textOverflow({ overflow: TextOverflow.Clip })
                .fontSize(20)
                .fontColor(Color.Yellow)
                .opacity(100)// 设置标题的透明度 不透明度设为100%,表示完全不透明
                .backgroundColor('#808080AA')// 背景颜色设为透明
                .zIndex(2)
                .onClick(() => {
                  router.pushUrl({
                    url: 'pages/WebViewPage',
                    params: { url: item.jumpUrl }
                  });
                })
            }
          }, (item: BannerItem) => item.operateId.toString())
        }
        .cachedCount(2)
        .index(1)
        .autoPlay(true)
        .interval(4000)
        .loop(true)
        .indicatorInteractive(true)
        .duration(1000)
        .itemSpace(0)
        .curve(Curve.Linear)
        .onChange((index: number) => {
          console.info(index.toString())
        })
        .onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
          console.info("index: " + index)
          console.info("current offset: " + extraInfo.currentOffset)
        })
        .height(180) // 设置高度

        Tabs({ barPosition: BarPosition.Start }) {
          TabContent() {
            // list组件
            Tabs0()
          }.tabBar(this.tabBuilder('推荐', 0))

          TabContent() {
            Tabs1()
          }.tabBar(this.tabBuilder('最新', 1))

          TabContent() {
          }.tabBar(this.tabBuilder('仓库', 2))

          TabContent() {
          }.tabBar(this.tabBuilder('组织', 3))
        }
        .animationDuration(0)
        .onChange((index: number) => {
          this.currentIndex = index
        })
      }
    }
  }
}

完整展示效果

开源协议

  • 协议选择:本项目采用MIT协议,允许用户自由使用、复制、修改和分发本软件,但需包含版权声明和许可声明。
  • 版权声明:版权所有者为冯腾飞,使用者在使用过程中需遵守MIT协议规定,未经允许不得移除或修改版权声明。

HAP 包

  • 目录位于:hap/entry-default-unsigned.hap

完整项目地址

源码地址

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
1
收藏 1
回复
举报
2条回复
按时间正序
/
按时间倒序
peterpan527
peterpan527

欢迎文章末尾下载源码

1
回复
2025-5-9 11:22:52
peterpan527
peterpan527 回复了 peterpan527
欢迎文章末尾下载源码

欢迎关注讨论技术问题

回复
2025-5-9 12:33:44
回复
    相关推荐