鸿蒙NEXT项目实战-百得知识库05

代码仓地址,大家记得点个star

IbestKnowTeach: 百得知识库基于鸿蒙NEXT稳定版实现的一款企业级开发项目案例。 本案例涉及到多个鸿蒙相关技术知识点: 1、布局 2、配置文件 3、组件的封装和使用 4、路由的使用 5、请求响应拦截器的封装 6、位置服务 7、三方库的使用和封装 8、头像上传 9、应用软件更新等https://gitee.com/xt1314520/IbestKnowTeach

消息页面开发

设计图

需求分析

消息组件的封装

复制代码
import { CommonConstant } from '../contants/CommonConstant'

@Component
  export struct MessageComponent {
    @State icon: ResourceStr = ''
    @State title: string = ''
    @Prop content: string
    @Prop unReadCount: number

    build() {
      Row({ space: 15 }) {
        Badge({
          count: this.unReadCount,
          position: BadgePosition.RightTop,
          style: { badgeSize: 15, badgeColor: '#FA2A2D' }
        }) {
          Image(this.icon)
            .width($r('app.float.common_width_small')).aspectRatio(1)
        }

        Column({ space: 10 }) {
          Text(this.title)
            .fontWeight(FontWeight.Medium)
            .fontSize($r('app.float.common_font_size_medium'))
          Text(this.content)
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
            .fontColor($r('app.color.common_gray'))
            .fontSize($r('app.float.common_font_size_small'))
        }.height(80).justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Start)
          .width('80%')
      }
      .width(CommonConstant.WIDTH_FULL)
        .height(80)
        .backgroundColor($r('app.color.common_white'))
        .borderRadius(15)
        .padding(10)
    }
  }

编写消息页面的接口方法

复制代码
import http from '../request/Request'
import { MessageInfoNew, PageVo, UserMessageCount, UserMessageInfo, UserMessagePageParam } from './MessageApi.type'

/**
 * 消息接口
 */
class MessageApi {
  /**
   * 获取最新消息
   */
  getMessageNew = (): Promise<Array<MessageInfoNew>> => {
    return http.get('/v1/message/new')
  }
  /**
   * 查询用户获得的消息总数量和未读
   */
  getUserMessageCount = (): Promise<UserMessageCount> => {
    return http.get('/v1/message/count')
  }
  /**
   * 用户根据消息分类查询分页的消息
   */
  getMessageList = (data: UserMessagePageParam): Promise<PageVo<UserMessageInfo>> => {
    return http.get('/v1/message/list?type=' + data.type + '&&page=' + data.page + '&&pageSize=' + data.pageSize)
  }
  /**
   * 查看消息详情
   */
  getMessageInfo = (data: number): Promise<UserMessageInfo> => {
    return http.get('/v1/message/info?id=' + data)
  }
}

const messageApi = new MessageApi();

export default messageApi as MessageApi;

/**
 * 最新消息
 */
export interface MessageInfoNew extends BaseTime {
  /**
   * 消息id
   */
  id: number
  /**
   * 消息标题
   */
  title: string
  /**
   * 消息内容
   */
  content: string
  /**
   * 消息类型'1' 系统消息 '2' 通知公告
   */
  type: string

}

/**
 * 时间
 */
export interface BaseTime {
  /**
   * 创建时间
   */
  createTime?: Date
  /**
   * 更新时间
   */
  updateTime?: Date
}

/**
 * 分页参数
 */
export interface PageParam {
  /**
   * 当前页
   */
  page?: number
  /**
   * 每一页展示的数据条数
   */
  pageSize?: number
}

/**
 * 消息总数量和未读
 */
export interface UserMessageCount {
  /**
   * 总消息数量
   */
  totalQuantity: number
  /**
   * 总未读数量
   */
  totalUnReadQuantity: number
  /**
   * 系统消息未读数量
   */
  systemUnReadQuantity: number
  /**
   * 通知公告消息未读数量
   */
  noticeUnReadQuantity: number
}


/**
 * 用户消息详情
 */
export interface UserMessageInfo extends BaseTime {
  /**
   * 用户消息id
   */
  id: number
  /**
   * 是否查看 '0'未查看 '1'查看
   */
  isLook: string
  /**
   * 消息id
   */
  messageId?: number
  /**
   * 消息标题
   */
  title: string
  /**
   * 消息内容
   */
  content: string
  /**
   * 消息类型'1' 系统消息 '2' 通知公告
   */
  type?: string
  /**
   * 创建时间
   */
  time: string

}

/**
 * 分页响应参数
 */
export interface PageVo<T> {
  current: number,
  size: number,
  total: number,
  records: Array<T>
}


/**
 * 用户根据消息分类查询所有的消息
 */
export interface UserMessagePageParam extends PageParam {
  /**
   * 消息类型
   */
  type: string
}

学习页面开发

设计图

需求分析

学习打卡

1、手机定位

在module.json5文件里面配置位置权限

代码向用户申请权限(弹窗)

复制代码
/**
 * 动态授权
 */
async aboutToAppear() {
  // 使用UIExtensionAbility:将common.UIAbilityContext 替换为common.UIExtensionContext
  const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  // 定义要申请的权限
  const permissions: Array<Permissions> = ['ohos.permission.APPROXIMATELY_LOCATION'];
  let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
  try {
    const data = await atManager.requestPermissionsFromUser(context, permissions);
    let grantStatus: Array<number> = data.authResults;
    let length: number = grantStatus.length;
    for (let i = 0; i < length; i++) {
      if (grantStatus[i] === 0) {
        // 获取位置信息
        this.getLocation()
      } else {
        // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
        showToast('用户必须授权才能访问当前定位功能')
        return;
      }
    }
  } catch (error) {
    showToast('用户必须授权才能访问当前定位功能')
  }
}

获取用户位置信息,获取经度纬度然后进行地理编码

复制代码
/**
 * 获取位置
 */
async getLocation() {
  let request: geoLocationManager.SingleLocationRequest = {
    'locatingPriority': geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED,
    'locatingTimeoutMs': 10000
  }
  try {
    // 调用getCurrentLocation获取当前设备位置,通过promise接收上报的位置
    const result = await geoLocationManager.getCurrentLocation(request)
    Logger.info('current location: ' + JSON.stringify(result));
    // 判断地理编码服务是否可用
    let isAvailable = geoLocationManager.isGeocoderAvailable();
    if (isAvailable) {
      // 地理编码服务可用
      let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest =
        { "latitude": result.latitude, "longitude": result.longitude, "maxItems": 1 };
      geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, (err, data) => {
        if (err) {
          Logger.error('getAddressesFromLocation err: ' + JSON.stringify(err));
        } else {
          Logger.info('getAddressesFromLocation data: ' + JSON.stringify(data));
          // 成功获取到位置信息
          this.location = data[0].administrativeArea + '' + data[0].subAdministrativeArea
          return
        }
      });
    } else {
      showToast('地理编码服务不可用')
    }
  } catch (err) {
    Logger.error("errCode:" + JSON.stringify(err));
    showToast("获取位置失败")
  }
}
2、日历组件

使用我们一开始项目准备阶段引入的鸿蒙三方库ibest-ui

复制代码
import { IBestCalendar } from "@ibestservices/ibest-ui";

// 日历
IBestCalendar({
  clock: true, // 设置 clock 为 true, 可开启打卡模式, 打卡模式下只能切换年月, 不能选择日期
  defaultSelectedDate: this.clockDate, // 打卡的日期是一个字符串数组
  clockSuccessText: '✔', // 已打卡展示的
  unClockText: '✖', // 未打卡展示的
})
3、打卡记录
4、编写学习打卡接口方法
复制代码
import http from '../request/Request'
import { LearnClockParam, PageParam, UserLearnClockData } from './LearnClockApi.type'
import { PageVo } from './MessageApi.type'

/**
 * 消息接口
 */
class LearnClockApi {
  /**
   * 学习打卡
   */
  learnClock = (data: LearnClockParam) => {
    return http.post('/v1/learn/clock', data)
  }
  /**
   * 查询用户已打卡的日期
   */
  listClockTime = (): Promise<Array<string>> => {
    return http.get('/v1/learn/listClockTime')
  }
  /**
   * 查询用户今日是否打卡
   */
  isClock = (): Promise<boolean> => {
    return http.get('/v1/learn/isClock')
  }
  /**
   * 分页查询用户打卡记录
   */
  pageListUserClock = (data: PageParam): Promise<PageVo<UserLearnClockData>> => {
    return http.get('/v1/learn/page?page=' + data.page + '&&pageSize=' + data.pageSize)
  }
}

const learnClockApi = new LearnClockApi();

export default learnClockApi as LearnClockApi;

/**
 * 学习打卡入参
 */
export interface LearnClockParam {
  /**
   * 打卡地点
   */
  location: string
  /**
   * 打卡内容
   */
  content: string
}

/**
 * 时间
 */
export interface BaseTime {
  /**
   * 创建时间
   */
  createTime?: Date
  /**
   * 更新时间
   */
  updateTime?: Date
}

/**
 * 分页参数
 */
export interface PageParam {
  /**
   * 当前页
   */
  page?: number
  /**
   * 每一页展示的数据条数
   */
  pageSize?: number
}


/**
 * 分页响应参数
 */
export interface PageVo<T> {
  current: number,
  size: number,
  total: number,
  records: Array<T>
}

/**
 * 用户打卡记录数据
 */
export interface UserLearnClockData extends BaseTime {

  /**
   * 学习打卡id
   */
  id: number
  /**
   * 打卡地点
   */
  location: string
  /**
   * 打卡内容
   */
  content: string
  /**
   * 用户昵称
   */
  nickname: string
  /**
   * 创建时间字符串格式
   */
  time: string

}

学习目标

1、目标整体进度

使用的是鸿蒙进度条组件

复制代码
// 环形进度条
Stack() {
  Progress({
    value: this.countData.completeQuantity,
    total: this.countData.totalQuantity,
    type: ProgressType.Ring
  })
    .width($r('app.float.common_width_medium'))
    .height($r('app.float.common_height_medium'))
    .color('#1698CE')
    .style({ strokeWidth: 8 })
  Text(this.countData.completeQuantity + '/' + this.countData.totalQuantity)
    .fontSize($r('app.float.common_font_size_tiny'))
}
2、添加目标

使用的是ibest-ui里面的IBestDialog组件

复制代码
IBestDialog({
  visible: $dialogVisible,
  title: "添加目标",
  showCancelButton: true,
  defaultBuilder: (): void => this.formInputContain(),
  beforeClose: async (action) => {
    if (action === 'cancel') {
      return true
    }
    const valueLength = this.inputTargetValue.trim().length;
    this.formInputError = !valueLength;
    if (!this.formInputError) {
      // 添加新目标
      await targetInfoApi.addTargetInfo({ content: this.inputTargetValue })
      showToast('添加目标成功')
      // 查询数据
      this.page = 1
      this.aboutToAppear()
      return true
    }
    return !this.formInputError
  }
})

@Builder
  formInputContain() {
    Column() {
      TextInput({ 'placeholder': '请输入目标内容,长度不能超过255字符' })
        .fontSize(14)
        .placeholderFont({ size: 14 })
        .onChange((value) => {
          this.inputTargetValue = value;
          this.formInputError = false
        })
      if (this.formInputError) {
        Text('目标内容不能为空')
          .width(CommonConstant.WIDTH_FULL)
          .textAlign(TextAlign.Start)
          .margin({
            top: 5,
            left: 5
          })
          .fontColor(Color.Red)
          .fontSize($r('app.float.common_font_size_small'))
          .transition({ type: TransitionType.Insert, opacity: 1 })
          .transition({ type: TransitionType.Delete, opacity: 0 })
      }

    }.width('90%').margin({ top: 15, bottom: 15 })
  }
3、完成、删除目标

左滑目标即可看到完成和删除按钮

使用的是ListItem.swipeAction方法

核心代码如下

复制代码
@Builder
  itemEnd(item: TargetInfoVo) {
    Row() {
      // 构建尾端滑出组件
      Button({ type: ButtonType.Circle }) {
        Image($r('app.media.icon_finish'))
          .width(40)
          .aspectRatio(1)
      }
      .onClick(async () => {
        // 完成未完成的目标
        if (item.status === '1') {
          showToast('当前目标已完成,无需重复点击')
          return
        }
        // 完成目标
        await targetInfoApi.completeTargetInfo({ id: item.id })
        showToast('目标已完成')
        router.replaceUrl({ url: RouterConstant.VIEWS_LEARN_TARGET })
      }).margin({ right: 10 })

      // 构建尾端滑出组件
      Button({ type: ButtonType.Circle }) {
        Image($r('app.media.icon_delete'))
          .width(40)
          .aspectRatio(1)
      }
      .margin({ right: 10 })
        .onClick(async () => {
          IBestDialogUtil.open({
            title: "提示",
            message: "是否确认删除当前目标?",
            showCancelButton: true,
            onConfirm: async () => {
              // 删除目标
              await targetInfoApi.deleteTargetInfo({ id: item.id })
              showToast('删除目标成功')
              router.replaceUrl({ url: RouterConstant.VIEWS_LEARN_TARGET })
            }
          })
        })
    }.justifyContent(FlexAlign.SpaceBetween)
  }
4、编写学习目标接口方法
复制代码
import http from '../request/Request'
import { PageParam, PageVo, TargetInfoAddParam,
         TargetInfoCompleteParam,
         TargetInfoCountVo,
         TargetInfoDeleteParam,
         TargetInfoEditParam,
         TargetInfoVo } from './TargetInfoApi.type'

/**
 * 目标内容接口
 */
class TargetInfoApi {
  /**
   * 获取用户整体目标完成进度统计
   */
  getTargetInfoCount = (): Promise<TargetInfoCountVo> => {
    return http.get('/v1/target/count')
  }
  /**
   * 分页查询用户整体目标
   */
  pageListTargetInfo = (data: PageParam): Promise<PageVo<TargetInfoVo>> => {
    return http.get('/v1/target/page?page=' + data.page + '&&pageSize=' + data.pageSize)
  }
  /**
   * 新增目标
   */
  addTargetInfo = (data: TargetInfoAddParam) => {
    return http.post('/v1/target/add', data)
  }

  /**
   * 修改目标
   */
  editTargetInfo = (data: TargetInfoEditParam) => {
    return http.post('/v1/target/edit', data)
  }

  /**
   * 完成目标
   */
  completeTargetInfo = (data: TargetInfoCompleteParam) => {
    return http.post('/v1/target/complete', data)
  }

  /**
   * 删除目标
   */
  deleteTargetInfo = (data: TargetInfoDeleteParam) => {
    return http.post('/v1/target/delete', data)
  }

}

const targetInfoApi = new TargetInfoApi();

export default targetInfoApi as TargetInfoApi;

/**
 * 用户整体目标完成进度统计
 */
export interface TargetInfoCountVo {
  /**
   * 总目标数量
   */
  totalQuantity: number
  /**
  * 完成目标的数量
  */
  completeQuantity: number
  /**
   * 更新时间
   */
  updateTime: string
}

/**
 * 新增目标
 */
export interface TargetInfoAddParam {
  /**
   * 目标内容
   */
  content: string

}

/**
 * 修改目标
 */
export interface TargetInfoEditParam {
  /**
   * 目标内容
   */
  content: string
  /**
   * 目标内容id
   */
  id: number

}

/**
 * 完成目标
 */
export interface TargetInfoCompleteParam {
  /**
   * 目标内容id
   */
  id: number

}

/**
 * 删除目标
 */
export interface TargetInfoDeleteParam {
  /**
   * 目标内容id
   */
  id: number

}


/**
 * 用户目标内容详情
 */
export interface TargetInfoVo extends BaseTime {
  /**
   * 目标内容id
   */
  id: number
  /**
   * 目标内容
   */
  content: string
  /**
   * 目标状态:0未完成 1完成
   */
  status: string
  /**
   * 创建时间字符串格式
   */
  time: string
}

/**
 * 时间
 */
export interface BaseTime {
  /**
   * 创建时间
   */
  createTime?: Date
  /**
   * 更新时间
   */
  updateTime?: Date
}

/**
 * 分页参数
 */
export interface PageParam {
  /**
   * 当前页
   */
  page?: number
  /**
   * 每一页展示的数据条数
   */
  pageSize?: number
}


/**
 * 分页响应参数
 */
export interface PageVo<T> {
  current: number,
  size: number,
  total: number,
  records: Array<T>
}

学习平台和面试平台

学习平台和面试平台跟首页差不多雷同,所以在这里就不说了

相关推荐
苏杰豪1 小时前
鸿蒙特效教程08-幸运大转盘抽奖
华为·harmonyos
苏杰豪1 小时前
鸿蒙特效教程07-九宫格幸运抽奖
华为·harmonyos
华研前沿标杆游学3 小时前
华为参访预约,团队考察体验黑科技之旅
华为
自不量力的A同学3 小时前
华为海思 CPU「麒麟 X90」曝光
华为
怀男孩10 小时前
鸿蒙NEXT开发之开屏广告实现
华为·harmonyos
MardaWang11 小时前
HarmonyOS开发,解决Kill server failed 报错问题
华为·harmonyos
北京自在科技12 小时前
蓝牙技术联盟中国实体成立!华为、小米发声支持本土化战略
华为·蓝牙·小米·bluetooth
冬冬小圆帽12 小时前
鸿蒙编译器安装运行教程
华为·harmonyos
李坤13 小时前
鸿蒙-封装吸边可拖拽视图
harmonyos·arkts·arkui
别说我什么都不会14 小时前
OpenHarmony源码分析之分布式软总线:authmanager模块(1)/设备认证连接管理
分布式·操作系统·harmonyos