鴻蒙NEXT開發案例:年齡計算

zhongcx發表於2024-11-14

【引言】

本案例的目標是開發一款年齡計算器應用,該應用能夠根據使用者輸入的出生日期,計算出使用者的實際年齡、虛歲、星座、生肖等資訊。同時,應用還將提供距離下次公曆和農曆生日的天數及星期等資訊。為了實現這些功能,我們將使用ArkTS和ArkUI作為開發語言,並藉助@nutpi/calendar-tool三方庫來處理複雜的日曆運算。

【環境準備】

電腦系統:windows 10

開發工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

工程版本:API 12

真機:mate60 pro

語言:ArkTS、ArkUI

三方庫:calendar-tool

【應用結構與實現】

1. 資料模型:首先定義一個Info類,用於封裝使用者的基本資訊,如公曆和農曆的年月日、星期、星座、生肖等。

2. 介面構建:應用介面主要由一個標題欄和多個展示區組成,每個展示區負責顯示不同的資訊,例如實際年齡、虛歲、星座等。介面佈局採用了Flex佈局,以確保良好的視覺效果和使用者體驗。

3. 邏輯處理:透過引入@nutpi/calendar-tool三方庫,我們可以輕鬆地完成日期轉換、星座計算等任務。此外,我們還需要編寫一些輔助函式,如計算實際年齡、虛歲、距離下次生日的天數等。

4. 使用者互動:為了讓使用者可以方便地輸入自己的出生日期,我們在介面上新增了一個日期選擇器,支援公曆和農曆之間的切換。當使用者選擇日期後,應用會自動更新所有相關資訊。

【完整程式碼】

使用的三方庫:ohpm install @nutpi/calendar-tool

參考:【https://ohpm.openharmony.cn/#/cn/detail/@nutpi%2Fcalendar-tool

程式碼:

import calendar from "@nutpi/calendar-tool" // 匯入日曆工具

class Info {
  sYear: number = 0 // 公曆年份
  sMonth: number = 0 // 公曆月份
  sDay: number = 0 // 公曆日期
  week: number = 0 // 星期幾(數字表示)
  weekZH: string = "" // 星期幾(中文表示)
  date: string = "" // 日期字串(公曆)
  zodiac: string = "" // 星座
  lYear: number = 0 // 農曆年份
  lMonth: number = 0 // 農曆月份
  lDay: number = 0 // 農曆日期
  isLeap: boolean = false // 是否為閏年
  lMonthZH: string = "" // 農曆月份(中文表示)
  lDayZH: string = "" // 農曆日期(中文表示)
  gzYearZH: string = "" // 年干支(中文表示)
  gzMonthZH: string = "" // 月干支(中文表示)
  gzDayZH: string = "" // 日干支(中文表示)
  animal: string = "" // 生肖
  term: string = "" // 節氣
  festival: string = "" // 節日
}

@Entry
  // 入口元件
@Component
  // 元件裝飾器
struct AgeCalculatorPage {
  @State season: string = '-' // 季節狀態
  @State realAge: number = 0 // 實際年齡
  @State lunarAge: number = 0 // 虛歲
  @State festival: string = '-' // 節日狀態
  @State weekday: string = '-' // 星期狀態
  @State isLunar: boolean = false // 是否為農曆
  @State zodiac: string = '-' // 星座狀態
  @State animal: string = '-' // 生肖狀態
  @State daysToNextGregorianBirthday: number = 0 // 距離下次公曆生日的天數
  @State daysToNextLunarBirthday: number = 0 // 距離下次農曆生日的天數
  @State dayOfWeekNextGregorianBirthday: number = 0 // 下次公曆生日的星期幾
  @State dayOfWeekNextLunarBirthday: number = 0 // 下次農曆生日的星期幾
  @State totalMonthsAlive: number = 0 // 總月數
  @State totalDaysAlive: number = 0 // 總天數
  @State totalHoursAlive: number = 0 // 總小時
  @State gregorianBirthDate: string = '-' // 公曆出生日期
  @State lunarBirthDate: string = '-' // 農曆出生日期
  private selectedBirthDate: Date = new Date('1989-01-17') // 選定的出生日期
  private weekdays: string[] = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'] // 星期陣列

  // 計算距離下次公曆生日的天數
  private calculateDaysToNextGregorianBirthday(birthDate: Date) {
    const nextBirthday = this.getNextBirthday(birthDate); // 獲取下一個公曆生日
    this.daysToNextGregorianBirthday =
      Math.ceil((nextBirthday.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)); // 計算天數差
  }

  // 計算下次公曆生日是星期幾
  private calculateNextGregorianBirthdayDayOfWeek(birthDate: Date) {
    const nextBirthday = this.getNextBirthday(birthDate); // 獲取下一個公曆生日
    this.dayOfWeekNextGregorianBirthday = nextBirthday.getDay(); // 獲取星期幾
  }

  // 計算在地球上的生活時間
  private calculateEarthTime(birthDate: Date) {
    const currentDate = new Date(); // 當前日期
    const years = currentDate.getFullYear() - birthDate.getFullYear(); // 計算年份差
    const months = currentDate.getMonth() - birthDate.getMonth(); // 計算月份差
    const days = currentDate.getDate() - birthDate.getDate(); // 計算日期差

    let adjustedYears = years; // 調整後的年份
    let adjustedMonths = months; // 調整後的月份

    if (days < 0) {
      adjustedMonths -= 1; // 如果天數為負,月份減1
    }
    if (months < 0) {
      adjustedYears -= 1; // 如果月份為負,年份減1
      adjustedMonths += 12; // 月份加12
    }

    this.totalMonthsAlive = adjustedYears * 12 + adjustedMonths; // 計算總月數
    this.totalDaysAlive = Math.floor((currentDate.getTime() - birthDate.getTime()) / (1000 * 60 * 60 * 24)); // 計算總天數
    this.totalHoursAlive = Math.floor((currentDate.getTime() - birthDate.getTime()) / (1000 * 60 * 60)); // 計算總小時
  }

  // 獲取季節
  private getSeason(date: Date) {
    const springStart = new Date(date.getFullYear(), 1, 3); // 春季開始日期
    const summerStart = new Date(date.getFullYear(), 4, 5); // 夏季開始日期
    const autumnStart = new Date(date.getFullYear(), 7, 7); // 秋季開始日期
    const winterStart = new Date(date.getFullYear(), 10, 7); // 冬季開始日期

    // 判斷當前日期屬於哪個季節
    if (date >= springStart && date < summerStart) {
      this.season = '春'; // 春季
    } else if (date >= summerStart && date < autumnStart) {
      this.season = '夏'; // 夏季
    } else if (date >= autumnStart && date < winterStart) {
      this.season = '秋'; // 秋季
    } else {
      this.season = '冬'; // 冬季
    }
  }

  // 計算下一個生日
  private getNextBirthday(birthDate: Date): Date {
    const currentDate = new Date(); // 當前日期
    let nextBirthday = new Date(currentDate.getFullYear(), birthDate.getMonth(), birthDate.getDate()); // 計算下一個生日

    if (currentDate > nextBirthday) {
      nextBirthday.setFullYear(nextBirthday.getFullYear() + 1); // 如果當前日期已過生日,年份加1
    }

    return nextBirthday; // 返回下一個生日
  }

  // 計算實際年齡
  private calculateRealAge(birthInfo: Info) {
    const today = new Date(); // 當前日期
    let realAge = today.getFullYear() - birthInfo.sYear; // 計算實際年齡

    // 判斷是否需要減去一年
    if (today.getMonth() < birthInfo.sMonth - 1 ||
      (today.getMonth() === birthInfo.sMonth - 1 && today.getDate() < birthInfo.sDay)) {
      realAge--; // 如果當前日期在生日之前,實際年齡減1
    }

    this.realAge = realAge; // 設定實際年齡
  }

  // 計算虛歲
  private calculateLunarAge() {
    const today = new Date(); // 當前日期
    const lunarNewYear = new Date(today.getFullYear(), 1, 22); // 假設農曆新年總是在公曆2月22日
    let lunarAge = this.realAge + 1; // 虛歲為實際年齡加1

    // 判斷是否需要減去一年
    if (today < lunarNewYear) {
      lunarAge--; // 如果當前日期在農曆新年之前,虛歲減1
    }

    this.lunarAge = lunarAge; // 設定虛歲
  }

  // 計算星座和生肖
  private calculateZodiacAndAnimal(birthInfo: Info) {
    this.zodiac = birthInfo.zodiac; // 設定星座
    this.animal = birthInfo.animal; // 設定生肖
  }

  // 計算星期和節日
  private calculateWeekdayAndFestival(birthInfo: Info) {
    this.weekday = birthInfo.weekZH; // 設定星期
    this.festival = birthInfo.festival; // 設定節日
  }

  // 計算下次生日
  private calculateNextBirthdays(birthInfo: Info) {
    this.calculateDaysToNextGregorianBirthday(this.selectedBirthDate); // 計算下次公曆生日的天數
    this.calculateNextGregorianBirthdayDayOfWeek(this.selectedBirthDate); // 計算下次公曆生日是星期幾

    let nextLunarBirthdayInfo: Info =
      calendar.getDateByLunar(new Date().getFullYear(), birthInfo.lMonth, birthInfo.lDay, false); // 獲取下一個農曆生日資訊

    // 如果當前日期已過農曆生日,獲取明年的農曆生日資訊
    if (new Date() > new Date(nextLunarBirthdayInfo.date)) {
      nextLunarBirthdayInfo =
        calendar.getDateByLunar(new Date().getFullYear() + 1, birthInfo.lMonth, birthInfo.lDay, false); // 獲取明年的農曆生日資訊
    }

    // 計算距離下次農曆生日的天數
    this.daysToNextLunarBirthday =
      Math.ceil((new Date(nextLunarBirthdayInfo.date).getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24));
    this.dayOfWeekNextLunarBirthday = nextLunarBirthdayInfo.week; // 設定下次農曆生日的星期幾
  }

  // 構建方法
  build() {
    Column() { // 建立列
      Text("年齡計算")// 顯示標題
        .width('100%')// 設定寬度為100%
        .height(44)// 設定高度為44
        .backgroundColor(Color.Orange)// 設定背景顏色為橙色
        .textAlign(TextAlign.Center)// 設定文字居中
        .fontColor(Color.White); // 設定字型顏色為白色

      Scroll() { // 建立可滾動區域
        Column() { // 建立列
          Row() { // 建立行
            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('週歲').fontSize(16).fontColor(Color.Gray) // 顯示“週歲”文字
              Text(`${this.realAge}`).fontSize(18).fontColor(Color.Black) // 顯示實際年齡
            }
            .width('200lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中

            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('公曆出生').fontSize(16).fontColor(Color.Gray) // 顯示“公曆出生”文字
              Text(this.gregorianBirthDate).fontSize(18).fontColor(Color.Black) // 顯示公曆出生日期
            }
            .width('425lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中

          }.width('650lpx').margin({ top: '30lpx' }).justifyContent(FlexAlign.SpaceBetween) // 設定行的寬度和間距

          Row() { // 建立行
            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('虛歲').fontSize(16).fontColor(Color.Gray) // 顯示“虛歲”文字
              Text(`${this.lunarAge}`).fontSize(18).fontColor(Color.Black) // 顯示虛歲
            }
            .width('200lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中

            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('農曆出生').fontSize(16).fontColor(Color.Gray) // 顯示“農曆出生”文字
              Text(this.lunarBirthDate).fontSize(18).fontColor(Color.Black) // 顯示農曆出生日期
            }
            .width('425lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中

          }.width('650lpx').margin({ top: '30lpx' }).justifyContent(FlexAlign.SpaceBetween) // 設定行的寬度和間距

          Row() { // 建立行
            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('星座').fontSize(16).fontColor(Color.Gray) // 顯示“星座”文字
              Text(this.zodiac).fontSize(18).fontColor(Color.Black) // 顯示星座
            }
            .width('200lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中

            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('下一個公曆生日').fontSize(16).fontColor(Color.Gray) // 顯示“下一個公曆生日”文字
              Text(`還有${this.daysToNextGregorianBirthday}天,那天是${this.weekdays[this.dayOfWeekNextGregorianBirthday]}`)
                .fontSize(18)// 顯示下一個公曆生日的天數和星期
                .fontColor(Color.Black)
            }
            .width('425lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中

          }.width('650lpx').margin({ top: '30lpx' }).justifyContent(FlexAlign.SpaceBetween) // 設定行的寬度和間距

          Row() { // 建立行
            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('生肖').fontSize(16).fontColor(Color.Gray) // 顯示“生肖”文字
              Text(this.animal).fontSize(18).fontColor(Color.Black) // 顯示生肖
            }
            .width('200lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中

            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('下一個農曆生日').fontSize(16).fontColor(Color.Gray) // 顯示“下一個農曆生日”文字
              Text(`還有${this.daysToNextLunarBirthday}天,那天是${this.weekdays[this.dayOfWeekNextLunarBirthday]}`)
                .fontSize(18)// 顯示下一個農曆生日的天數和星期
                .fontColor(Color.Black)
            }
            .width('425lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中
          }.width('650lpx').margin({ top: '30lpx' }).justifyContent(FlexAlign.SpaceBetween) // 設定行的寬度和間距

          Row() { // 建立行
            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('季節').fontSize(16).fontColor(Color.Gray) // 顯示“季節”文字
              Text(this.season).fontSize(18).fontColor(Color.Black) // 顯示季節
            }
            .width('200lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓角
            .justifyContent(FlexAlign.Center) // 設定內容居中

            Column({ space: '5lpx' }) { // 建立列,設定間距
              Text('出生當天節日').fontSize(16).fontColor(Color.Gray).padding({ left: 5, right: 5 }) // 顯示“出生當天節日”文字
              Text(this.festival || '-').fontSize(18).fontColor(Color.Black) // 顯示節日,若無則顯示‘-’
            }
            .width('425lpx') // 設定寬度
            .height('150lpx') // 設定高度
            .borderColor(Color.Gray) // 設定邊框顏色
            .borderWidth(1) // 設定邊框寬度
            .borderRadius(5) // 設定邊框圓
            .justifyContent(FlexAlign.Center) // 設定內容居中

          }.width('650lpx').margin({ top: '30lpx' }).justifyContent(FlexAlign.SpaceBetween) // 設定行的寬度和間距

          Column() { // 建立列
            Text(`您在地球生活了`).fontSize(16).fontColor(Color.Gray).margin({ top: '30lpx' }) // 顯示“您在地球生活了”文字

            Row() { // 建立行
              Column({ space: '5lpx' }) { // 建立列,設定間距
                Text('總月數').fontSize(16).fontColor(Color.Gray) // 顯示“總月數”文字
                Text(`${this.totalMonthsAlive}`).fontSize(18).fontColor(Color.Black) // 顯示總月數
              }
              .width('200lpx') // 設定寬度
              .height('120lpx') // 設定高度
              .justifyContent(FlexAlign.Center) // 設定內容居中

              Column({ space: '5lpx' }) { // 建立列,設定間距
                Text('總天數').fontSize(16).fontColor(Color.Gray) // 顯示“總天數”文字
                Text(`${this.totalDaysAlive}`).fontSize(18).fontColor(Color.Black) // 顯示總天數
              }
              .width('200lpx') // 設定寬度
              .height('120lpx') // 設定高度
              .justifyContent(FlexAlign.Center) // 設定內容居中

              Column({ space: '5lpx' }) { // 建立列,設定間距
                Text('總小時').fontSize(16).fontColor(Color.Gray) // 顯示“總小時”文字
                Text(`${this.totalHoursAlive}`).fontSize(18).fontColor(Color.Black) // 顯示總小時
              }
              .width('200lpx') // 設定寬度
              .height('120lpx') // 設定高度
              .justifyContent(FlexAlign.Center) // 設定內容居中

            }.width('650lpx').justifyContent(FlexAlign.SpaceBetween) // 設定行的寬度和間距

          }
          .width('650lpx') // 設定寬度
          .margin({ top: '30lpx' }) // 設定上邊距
          .justifyContent(FlexAlign.SpaceBetween) // 設定內容居中
          .borderColor(Color.Gray) // 設定邊框顏色
          .borderWidth(1) // 設定邊框寬度
          .borderRadius(5) // 設定邊框圓角
        }
      }.width('100%').layoutWeight(1) // 設定寬度為100%,權重為1

      Row() { // 建立行
        Text(`請選擇出生${this.isLunar ? '(農曆)' : '(公曆)'}:`).fontColor(Color.Orange).fontSize(18) // 顯示選擇出生日期的文字
        Button('切換公曆農曆')// 顯示切換按鈕
          .backgroundColor(Color.Orange)// 設定背景顏色為橙色
          .margin({ top: 30, bottom: 30 })// 設定上下邊距
          .borderRadius(5)// 設定邊框圓角
          .onClick(() => { // 點選事件
            this.isLunar = !this.isLunar // 切換公曆農曆狀態
          })
      }.width('650lpx').justifyContent(FlexAlign.SpaceBetween) // 設定行的寬度和間距

      DatePicker({
        // 建立日期選擇器
        start: new Date('1900-1-1'), // 設定起始日期
        end: new Date('2100-1-1'), // 設定結束日期
        selected: this.selectedBirthDate // 設定選定的出生日期
      })
        .height('350lpx')// 設定高度
        .disappearTextStyle({ color: Color.Gray, font: { size: '16fp', weight: FontWeight.Bold } })// 設定消失文字樣式
        .textStyle({ color: Color.Black, font: { size: '18fp', weight: FontWeight.Normal } })// 設定文字樣式
        .selectedTextStyle({ color: Color.Orange, font: { size: '26fp', weight: FontWeight.Regular } })// 設定選定文字樣式
        .lunar(this.isLunar)// 設定是否為農曆
        .onDateChange((value: Date) => { // 日期選擇器變化時的處理
          let birthInfo: Info =
            calendar.getDateBySolar(value.getFullYear(), value.getMonth() + 1, value.getDate()); // 獲取出生日期資訊
          this.selectedBirthDate = value; // 設定選定的出生日期
          //公曆出生
          this.gregorianBirthDate = `${birthInfo.sYear}年${birthInfo.sMonth}月${birthInfo.sDay}日` // 設定公曆出生日期
          //農曆出生
          this.lunarBirthDate = `${birthInfo.lYear}年${birthInfo.lMonthZH}${birthInfo.lDayZH}` // 設定農曆出生日期
          this.calculateRealAge(birthInfo); // 計算實際年齡
          this.calculateLunarAge(); // 計算虛歲
          this.calculateZodiacAndAnimal(birthInfo); // 計算星座和生肖
          this.calculateWeekdayAndFestival(birthInfo); // 計算星期和節日
          this.calculateNextBirthdays(birthInfo); // 計算下次生日
          this.getSeason(this.selectedBirthDate); // 獲取季節
          this.calculateEarthTime(value); // 計算在地球上的生活時間
        })
    }
    .height('100%') // 設定高度為100%
    .width('100%') // 設定寬度為100%
  }
}

  

相關文章