<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: xiaoyaolihua</title>
    <description>The latest articles on DEV Community by xiaoyaolihua (@xiaoyaolihua).</description>
    <link>https://dev.to/xiaoyaolihua</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3305757%2F615ee8a3-23e3-45f3-a0e8-8dd4d9faa074.png</url>
      <title>DEV Community: xiaoyaolihua</title>
      <link>https://dev.to/xiaoyaolihua</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xiaoyaolihua"/>
    <language>en</language>
    <item>
      <title>HarmonyOS NEXT-Axios is used to encapsulate different interface types according to the return type o</title>
      <dc:creator>xiaoyaolihua</dc:creator>
      <pubDate>Mon, 30 Jun 2025 05:21:55 +0000</pubDate>
      <link>https://dev.to/xiaoyaolihua/harmonyos-next-axios-is-used-to-encapsulate-different-interface-types-according-to-the-return-type-o-33i9</link>
      <guid>https://dev.to/xiaoyaolihua/harmonyos-next-axios-is-used-to-encapsulate-different-interface-types-according-to-the-return-type-o-33i9</guid>
      <description>&lt;p&gt;​&lt;br&gt;
We use axios to encapsulate an API to facilitate processing of interface data and obtaining interfaces, what should we do when the data type of the interface does not meet the ResponseType specification? followed by sample code (with explanations and comments)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from '@ohos/axios'
import { auth } from './Auth'
import { promptAction } from '@kit.ArkUI'
import { logger } from './Logger'

// Increase the type
export interface APIErrorType {
  message: string
  msg: string
  code: string
}
//The type of the interface returned
export interface TestResponseType&amp;lt;T&amp;gt; {
  Test_data1: string;
  data: T;
  Test_data3: string
  /**
   * 10000-Succeed; Other - failed
   */
  Test_code: string;
  Test_msg: null;
}

// Base address
export const baseURL = 'http://test.changfunfly.com/'

// 1. Instantiate the Common Configuration
const instance = axios.create({
  baseURL,
  timeout: 5000
})

// 2. Configure a request interceptor
instance.interceptors.request.use((config: InternalAxiosRequestConfig) =&amp;gt; {
  // Unified Token Carrier
  const user = auth.getUser()
  // logger.info('Is the request interceptor triggered?？')
  config.headers.company_type = "2"
  config.headers.sign = "himyidea_test_sign"
  config.headers.envir = "HarmonyOS"
  if (user?.token) {
    // config.headers.Authorization = `Bearer ${user.token}`
    config.headers.access_token = user.token
  }
  // logger.info('config.headers Request parameters:' + config.headers)
  // logger.info('config.headers.Authorization Request parameter 2:' + JSON.stringify(config.headers.Authorization))
  return config
}, (error: AxiosError) =&amp;gt; {
  return Promise.reject(error)
})


// 3. Add a response blocker
//Unified error handling, etc
instance.interceptors.response.use((response: AxiosResponse) =&amp;gt; {

  return response.data
}, (error: AxiosError&amp;lt;APIErrorType&amp;gt;) =&amp;gt; {
  // 3.1 Handle token expiration
  if (error.response?.status === 401) {
    promptAction.openToast({ message: 'If your login has expired, please log in again' })
    auth.removeUser()
    // Here to log out the information needs to be captured
    // router.pushUrl({ url: 'pages/Index' })
  }
  // 3.2 The processing did not find the requested resource
  else if (error.response?.status === 404) {
    promptAction.openToast({ message: 'The requested resource does not exist' })
  }
  // 3.3There is no access to the processing
  else if (error.response?.status === 403) {
    promptAction.openToast({ message: 'There are no permissions' })
  }
  // 3.4 The request was processed in an incorrect way
  else if (error.response?.status === 405) {
    promptAction.openToast({ message: 'The request method is not allowed (e.g., POST access to interfaces that only support GET.)）' })
  }
 3.5 Timeout for processing requests
else if (error.response?. status === 408) {
promptAction.openToast({ message: 'Request timed out' })
}
3.6 Handling throttling
else if (error.response?. status === 429) {
promptAction.openToast({ message: 'Requests are too frequent (throttling)' })
}
3.7 Handling Server Errors
else if (error.response?. status === 500) {
promptAction.openToast({ message: 'Server Error' })
}
3.8 Handling Gateway Errors
else if (error.response?. status === 502) {
promptAction.openToast({ message: 'Gateway Error' })
}
3.9 Processing Server Unavailable
else if (error.response?. status === 503) {
promptAction.openToast({ message: 'Server unavailable (ex: under maintenance)' })
}
3.10 Processing Gateway Timeout
else if (error.response?. status === 504) {
promptAction.openToast({ message: 'Gateway timeout' })
  } else {
    promptAction.openToast({ message: error?.response?.data.message })
  }
  return Promise.reject(error)
})

// Generic data object types of APIs
export interface HttpResponse&amp;lt;T&amp;gt; {
  code: string
  msg: string
  result: T
}

export type ResponseType&amp;lt;T&amp;gt; = AxiosResponse&amp;lt;HttpResponse&amp;lt;T&amp;gt;&amp;gt;


export class RequestAxios {
  // get -&amp;gt; params -&amp;gt; { params: {} }
  static get&amp;lt;T&amp;gt;(url: string, config?: AxiosRequestConfig): Promise&amp;lt;ResponseType&amp;lt;T&amp;gt;&amp;gt; {
    return instance.get&amp;lt;null, ResponseType&amp;lt;T&amp;gt;&amp;gt;(url, config)
  }

  static post&amp;lt;T, D&amp;gt;(url: string, data?: D): Promise&amp;lt;ResponseType&amp;lt;T&amp;gt;&amp;gt; {
    return instance.post&amp;lt;null, ResponseType&amp;lt;T&amp;gt;, D&amp;gt;(url, data)
  }


  static delete&amp;lt;T&amp;gt;(url: string, config?: AxiosRequestConfig): Promise&amp;lt;ResponseType&amp;lt;T&amp;gt;&amp;gt; {
    return instance.delete&amp;lt;null, ResponseType&amp;lt;T&amp;gt;&amp;gt;(url, config)
  }

  static put&amp;lt;T&amp;gt;(url: string, data?: object): Promise&amp;lt;ResponseType&amp;lt;T&amp;gt;&amp;gt; {
    return instance.put&amp;lt;null, ResponseType&amp;lt;T&amp;gt;&amp;gt;(url, data)
  }

  static post_cf&amp;lt;T, D&amp;gt;(url: string, data?: D): Promise&amp;lt;CFResponseType&amp;lt;T&amp;gt;&amp;gt; {
    return instance.post&amp;lt;null, CFResponseType&amp;lt;T&amp;gt;, D&amp;gt;(url, data)
  }

  // get -&amp;gt; params -&amp;gt; { params: {} }
  static get_cf&amp;lt;T&amp;gt;(url: string, config?: AxiosRequestConfig): Promise&amp;lt;CFResponseType&amp;lt;T&amp;gt;&amp;gt; {
    return instance.get&amp;lt;null, CFResponseType&amp;lt;T&amp;gt;&amp;gt;(url, config)
  }

  /**
   * post_cf_object(You don't need to encapsulate the request parameters)
   * @param url
   * @param data
   * @returns
   */
  static post_cf_object&amp;lt;T&amp;gt;(url: string, data?: object): Promise&amp;lt;CFResponseType&amp;lt;T&amp;gt;&amp;gt; {
    return instance.post&amp;lt;null, CFResponseType&amp;lt;T&amp;gt;&amp;gt;(url, data)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Code explanation: The axios network request encapsulation library based on HarmonyOS mainly implements the following functions: create an instance with the basic URL and timeout settings through axios.create(); Fixed request headers such as company_type and sign are added to the request interceptor, and user tokens are dynamically injected. In the response interceptor, various HTTP error status codes (401/404/500, etc.) are systematically processed, promptAction is used to display friendly error prompts, and the token clearance operation is performed especially for the 401 state. The RequestAxios class provides typed request methods such as GET/POST/DELETE/PUT, and supports different response data structures (including standard format and TestResponseType special format) through generics, and finally realizes the unified configuration, identity authentication, error handling, and type-safe invocation of network requests. ）&lt;/p&gt;

&lt;p&gt;​&lt;/p&gt;

</description>
      <category>harmonyos</category>
    </item>
    <item>
      <title>HarmonyOS NEXT-TextInput text input box notes</title>
      <dc:creator>xiaoyaolihua</dc:creator>
      <pubDate>Mon, 30 Jun 2025 05:20:35 +0000</pubDate>
      <link>https://dev.to/xiaoyaolihua/harmonyos-next-textinput-text-input-box-notes-1eg6</link>
      <guid>https://dev.to/xiaoyaolihua/harmonyos-next-textinput-text-input-box-notes-1eg6</guid>
      <description>&lt;p&gt;​&lt;br&gt;
The author encountered a lot of interesting things when using the TextInput text input box, today 2025/6/30, to make a record. Tell everyone about some strange problems and solutions.&lt;/p&gt;

&lt;p&gt;1.TextInput, the text input box wants an underscore. Add a .showUnderline(true) attribute to him, and the text input box will be underlined, as shown in the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; TextInput({ placeholder: 'Prompt text content' })
        .width(350)
        .showUnderline(true)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The text input box is the same as the large white background. It's simple and beautiful, and the white looks very clean.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scene reproduction, the text input box above should be entered as a password, and if you want an eye style, how to achieve it? It's simple, just need us to add&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;.type(InputType.Password)&lt;br&gt;
property, the sample code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TextInput({ placeholder: 'Prompt text content' })
        .width(350)
        .showUnderline(true)
        .type(InputType.Password)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the code, and find that although our text input box is the logic of the password input box, but the text input box has become gray, particularly ugly, and compared with our previous white, the style is not good-looking, so how do we modify it? It's actually very simple, just need to add&lt;/p&gt;

&lt;p&gt;.backgroundColor($r('sys.color.comp_background_list_card'))&lt;br&gt;
The property is good, and the sample code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TextInput({ placeholder: 'Prompt text content' })
        .width(350)
        .showUnderline(true)
        .type(InputType.Password)
        .backgroundColor($r('sys.color.comp_background_list_card'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expand:&lt;/p&gt;

&lt;p&gt;1.The placeholder text of TextInput will automatically disappear when the user starts typing, and re-display it after the input box is cleared and loses focus, without additional code control&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/local"&gt;@local&lt;/a&gt; inputText: string = '';&lt;br&gt;
TextInput({ placeholder: 'Enter text' })&lt;br&gt;
  .onChange((value) =&amp;gt; this.inputText = value) // Real-time status updates&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;There is an additional captcha input mode in the input box in API20 InputType.ONE_TIME_CODE, you can try it, which is very interesting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After setting maxLength, clicking on the input method again when the input reaches the upper limit will not trigger onChange.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So how do we solve this problem?&lt;/p&gt;

&lt;p&gt;The sample code is as follows, (we need to prompt after 20 characters are exceeded, and then truncate the excess characters, and the maximum number of characters is 1 character overrun)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.maxLength(21) // Set the limit of 1 character exceeded
.onChange((value) =&amp;gt; {
  if (value.length &amp;gt; 20) {
    promptAction.showToast({ message: 'The upper limit has been reached' }); // A pop-up prompt pops up
    this.inputText = value.substring(0, 20); // Truncate extra-long characters
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.onChange: Triggered when the input content changes (Note: It will not be triggered when the maxLength is exceeded)&lt;/p&gt;

&lt;p&gt;We can implement bindings in onChange, or we can use the new V2 syntax!! , add after the variable to implement two-way binding, note that the variable must be a state variable&lt;/p&gt;

&lt;p&gt;5.onSubmit: Triggered when the keyboard submit button is clicked, the custom keyboard needs to be bound to the enter key type through keyboardType&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Style customization: placeholderColor()/placeholderFont(): modify placeholder style 24. caretColor(): Sets the cursor color&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inline mode and password mode do not display counters&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By leveraging state management, event listening, and style customization, we can build an efficient input experience that conforms to HarmonyOS design specifications while avoiding anomalous behavior in boundary conditions.&lt;/p&gt;

&lt;p&gt;​&lt;/p&gt;

</description>
      <category>harmonyos</category>
    </item>
    <item>
      <title>HarmonyOS NEXT- project actual combat calendar, novice tutorial 3</title>
      <dc:creator>xiaoyaolihua</dc:creator>
      <pubDate>Sun, 29 Jun 2025 14:53:52 +0000</pubDate>
      <link>https://dev.to/xiaoyaolihua/harmonyos-next-project-actual-combat-calendar-novice-tutorial-3-1oa8</link>
      <guid>https://dev.to/xiaoyaolihua/harmonyos-next-project-actual-combat-calendar-novice-tutorial-3-1oa8</guid>
      <description>&lt;p&gt;​&lt;br&gt;
In the previous article, we have finished displaying the calendar on the interface, so let's start.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calendar, calendar color style, size and other styles improved.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The sample code is as follows (including explanation and comments):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { i18n } from '@kit.LocalizationKit';


interface CalenderType {
  Calendar?: string;
  Lunar?: string;
  Color?: Color | string // background color
  index?: number //Determine whether you can click or not, which is the number of the month
  fontColor?: string | Color // font color
}
@Entry
@Component
struct Index {
  private scrollerForScroll: Scroller = new Scroller();
  private scrollerForList: Scroller = new Scroller();

  @State listPosition: number = 0; // 0 means scrolling to the top of the List, 1 means intermediate value, and 2 means scrolling to the bottom of the List.
  //Design lunar calendar time

  getLunarDate(year: number, month: number, day: number) {
    // 1. Set Gregorian calendar date
    const gregorianCalendar = i18n.getCalendar("zh-Hans", "gregory");
    gregorianCalendar.set(year, month - 1, day); // Months need to be minus 1

    // 2. Convert to lunar calendar
    const lunarCalendar = i18n.getCalendar("zh-Hans", "chinese");
    lunarCalendar.setTime(gregorianCalendar.getTimeInMillis());

    // 3. Get the lunar calendar value
    const lunarYear = lunarCalendar.get("year");
    const lunarMonth = lunarCalendar.get("month");
    const lunarDay = lunarCalendar.get("date");

    // 4. Convert to Chinese  lunar calendar
    const lunarMonths = ['New Year', 'February', 'March', 'April', 'May', 'June',
      'July', 'August', 'September', 'October', 'Winter Moon', 'Wax Moon'];
    const lunarDays = ['Lunar 1st', '2nd Lunar Month', '3rd Lunar Synchronous', '4th Lunar Synchronous', '5th Lunar Attainment', 'Lunar 6', 'Lunar 7', 'Lunar 8', 'Lunar 9', 'Lunar 10',
      'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen', 'Twenty',
      'Twenty-one', 'Twenty-two', 'Twenty-three', 'Twenty-four', 'Twenty-five', 'Twenty-six', 'Twenty-Seven', 'Twenty-Eight', 'Twenty-Nine', 'Thirty'];
    return lunarDays[lunarDay - 1]
  }


  getTime(index: number = 0) {
    let arr: CalenderType[] = []
    const date = new Date();
    const year = date.getFullYear();
    const month = date.getMonth() + 1 + index;
    const day = date.getDate();
    const week = date.getDay();
    // Example: Get the day of the week on Thursday, June 5, 2025
    const targetDate = new Date(year, month - 1, 1); // Note: Months are counted from 0 (5 for June)
    const dayIndex = targetDate.getDay(); // Back 4 (Thursday)
    const weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"
      , "Friday", "Saturday"];
    for (let i = 0; i &amp;lt; dayIndex; i++) {
      arr.push({ Color: Color.Transparent, index: -1 })
    }
    // Get how many days there are in a month
    const days = new Date(year, month, 0).getDate();
    for (let i = 1; i &amp;lt; days + 1; i++) {
      if ((dayIndex + i - 1) % 7 == 0 || (dayIndex + i - 1) % 7 == 6) {
        arr.push({
          Calendar: i + '',
          Lunar: this.getLunarDate(year, month, i),
          Color: Color.White,
          index: i,
          fontColor: '#FF4A24'
        })
      } else {
        arr.push({
          Calendar: i + '',
          Lunar: this.getLunarDate(year, month, i),
          Color: Color.White,
          index: i,
          fontColor: '#000000'
        })
      }
    }
    return arr
  }

  aboutToAppear(): void {
    this.getTime()
    this.getLunarDate(2025, 6, 7)
  }

  build() {
    Column() {
      // Nav head navigation bar
      Row() {
        Image($r('sys.media.ohos_ic_public_arrow_left'))
          .width(28)
          .aspectRatio(1)
          .fillColor(Color.Black)
          .onClick(() =&amp;gt; {

          })
        Text('Calendar')
          .fontSize(18)
        Column()
          .width(28)
      }
      .padding(8)
      .width('100%')
      .backgroundColor('#50cccccc')
      .justifyContent(FlexAlign.SpaceBetween)

      // Date arrangement
      Row() {
        Text('Sunday')
          .fontColor('#FF4A24')
          .layoutWeight(1)
          .fontSize(12)
        Text('Monday')
          .layoutWeight(1)
          .fontSize(12)

        Text('Tuesday')
          .layoutWeight(1)
          .fontSize(12)

        Text('Wednesday')
          .layoutWeight(1)
          .fontSize(12)
        Text('Thursday')
          .fontSize(12)
          .layoutWeight(1)
        Text('Friday')
          .fontSize(12)
          .layoutWeight(1)
        Text('Saturday')
          .fontSize(12)
          .layoutWeight(1)
          .fontColor('#FF4A24')
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
      .padding({ top: 15, bottom: 15 })
      //Scroll through the calendar
      Scroll(this.scrollerForScroll) {
        Column() {
          CalenderComp({ arr: this.getTime(), index: 0 })
          CalenderComp({ arr: this.getTime(1), index: 1 })
          CalenderComp({ arr: this.getTime(2), index: 2 })
        }
      }
      .width("100%")
      .layoutWeight(1)
    }
    .width('100%')
    .layoutWeight(1)
  }
}

@Component
struct CalenderComp {
  @Prop arr: CalenderType[] = []
  @Prop index: number = 0
  year: number = 0
  month: number = 0

  aboutToAppear(): void {
    this.year = (new Date()).getFullYear();
    this.month = (new Date()).getMonth() + 1 + this.index
  }

  build() {
    Column({ space: 5 }) {
      Text(this.year + 'year' + this.month + 'month')
        .fontSize(18)
        .width('100%')
        .padding(8)
        .textAlign(TextAlign.Center)
      //   dividingLine
      Divider()
        .color(Color.Red)
        .strokeWidth(2)
        .margin({ top: 1, bottom: 10 })
      Grid() {
        ForEach(this.arr, (item: CalenderType) =&amp;gt; {
          GridItem() {
            Column() {
              Text(item.Calendar)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .fontColor(item.fontColor)
              Text(item.Lunar)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .fontColor(item.fontColor)
            }

            // .margin(15)
          }
          .layoutWeight(1)
          .aspectRatio(1) //'#9DC3E6'
          .backgroundColor(item.Color)
        })
      }
      .rowsGap(10)
      .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: This time the focus is on enhancing the visual presentation and interactive foundation of the calendar, and the core optimization includes four aspects: first, the CalenderType interface is extended to add style control fields - Color (background color), fontColor (text color) and index (date index); Secondly, the style is dynamically set in the date generation logic: the text of the weekend date (Saturday/Sunday) is set to red (#FF4A24), the weekday is black, the background of the blank date is transparent and marked as an invalid index (index=-1), and the valid date is unified with a white background. Third, optimize the layout of calendar grids - add aspectRatio(1) to force the square ratio, set layoutWeight(1) to achieve monospaced distribution, increase rowsGap(10) line spacing, and dynamically apply text colors; Finally, adjust the vertical spacing of the week title to 15 units, while keeping the month title and navigation bar style unchanged. The current implementation gives the calendar grid a neat square layout, with weekend dates prominently displayed in red, laying the foundation for subsequent date selection and event marking. ）&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jfbgi04oa77wsqjj4hi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jfbgi04oa77wsqjj4hi.png" alt="Image description" width="321" height="693"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Expand the single-point calendar (store the click data of the calendar and display the effect).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { i18n } from '@kit.LocalizationKit';
import { router } from '@kit.ArkUI';


interface CalenderType {
  Calendar: string| null;
  Lunar: string| null;
  Color: Color | ResourceStr| null // background color
  index: number| null //Determine whether you can click or not, which is the number of the month
  fontColor: string | Color| null // font color
  Time: Date | null
}
@ObservedV2
class CalenderTypeModel implements CalenderType {
  @Trace Calendar: string | null = null
  @Trace Lunar: string | null = null
  @Trace Color: Color | ResourceStr | null = null
  @Trace index: number | null = null
  @Trace fontColor: string | Color | null = null
  @Trace  Time: Date | null = null

  constructor(model: CalenderType) {
    this.Calendar = model.Calendar
    this.Lunar = model.Lunar
    this.Color = model.Color
    this.index = model.index
    this.fontColor = model.fontColor
    this.Time = model.Time
  }
}

@Entry
@ComponentV2
struct Index {
  private scrollerForScroll: Scroller = new Scroller();
  @Local calenderArr: CalenderType[][]=[]
  @Local listPosition: number = 0; // 0 means scrolling to the top of the List, 1 means intermediate value, and 2 means scrolling to the bottom of the List.
  //Design lunar calendar time
  /** Calendar mode 0 for single-point calendar, 1 for dotted calendar, 2 for multi-select calendar */
  @Local calendarMode: number = 0; //Calendar mode 0 means a single-point calendar, 1 means a dotted calendar, and 2 means a multi-select calendar


  getDate(year: number, month: number, day: number) {
    // 1. Set Gregorian calendar date
    const gregorianCalendar = i18n.getCalendar("zh-Hans", "gregory");
    gregorianCalendar.set(year, month - 1, day); // Months need to be minus 1

    // 2. Convert to lunar calendar
    const lunarCalendar = i18n.getCalendar("zh-Hans", "chinese");
    lunarCalendar.setTime(gregorianCalendar.getTimeInMillis());

    // 3. Get the lunar calendar value
    const lunarDay = lunarCalendar.get("date");

    // 4. Convert to Chinese  lunar calendar

    const lunarDays = ['Lunar 1st', '2nd Lunar Month', '3rd Lunar Synchronous', '4th Lunar Synchronous', '5th Lunar Attainment', 'Lunar 6', 'Lunar 7', 'Lunar 8', 'Lunar 9', 'Lunar 10',
      'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen', 'Twenty',
      'Twenty-one', 'Twenty-two', 'Twenty-three', 'Twenty-four', 'Twenty-five', 'Twenty-six', 'Twenty-Seven', 'Twenty-Eight', 'Twenty-Nine', 'Thirty'];
    return lunarDays[lunarDay - 1]
  }


  getTime(index: number = 0) {
    let arr: CalenderType[] = []
    const date = new Date();
    const year = date.getFullYear();
    const month = date.getMonth() + 1 + index;
    // Example: Get the day of the week on Thursday, June 5, 2025
    const targetDate = new Date(year, month - 1, 1); // Note: Months are counted from 0 (5 for June)
    const dayIndex = targetDate.getDay(); // Back 4 (Thursday)
    for (let i = 0; i &amp;lt; dayIndex; i++) {
      arr.push(({
        Color: Color.Transparent,
        index: -1,
        Calendar: null,
        Lunar: null,
        fontColor: null,
        Time: null
      }))
    }
    // Get how many days there are in a month
    const days = new Date(year, month, 0).getDate();
    for (let i = 1; i &amp;lt; days + 1; i++) {
      if ((dayIndex + i - 1) % 7 == 0 || (dayIndex + i - 1) % 7 == 6) {
        arr.push(({
          Calendar: i + '',
          Lunar: this.getDate(year, month, i),
          Color: Color.White,
          index: i,
          fontColor: '#FF4A24',
          Time: new Date(year, month - 1, i)
        }))
      } else {
        arr.push(({
          Calendar: i + '',
          Lunar: this.getDate(year, month, i),
          Color: Color.White,
          index: i,
          fontColor: '#000000',
          Time: new Date(year, month - 1, i)
        }))
      }
    }
    return arr
  }

  aboutToAppear(): void {
    this.calenderArr.push(this.getTime(0))
    this.calenderArr.push(this.getTime(1))
    this.calenderArr.push(this.getTime(2))
    const param=router.getParams()
    if (param === 'A la carte calendar') {
      this.calendarMode = 0
    }
  }

  build() {
    Column() {
      // Nav head navigation bar
      Row() {
        Image($r('sys.media.ohos_ic_public_arrow_left'))
          .width(28)
          .aspectRatio(1)
          .fillColor(Color.Black)
          .onClick(() =&amp;gt; {

          })
        Text('Calendar')
          .fontSize(18)
        Column()
          .width(28)
      }
      .padding(8)
      .width('100%')
      .backgroundColor('#50cccccc')
      .justifyContent(FlexAlign.SpaceBetween)

      // Date arrangement
      Row() {
        Text('Sunday')
          .fontColor('#FF4A24')
          .layoutWeight(1)
          .fontSize(12)
        Text('Monday')
          .layoutWeight(1)
          .fontSize(12)

        Text('Tuesday')
          .layoutWeight(1)
          .fontSize(12)

        Text('Wednesday')
          .layoutWeight(1)
          .fontSize(12)
        Text('Thursday')
          .fontSize(12)
          .layoutWeight(1)
        Text('Friday')
          .fontSize(12)
          .layoutWeight(1)
        Text('Saturday')
          .fontSize(12)
          .layoutWeight(1)
          .fontColor('#FF4A24')
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
      .padding({ top: 15, bottom: 15 })
      //Scroll through the calendar
      Scroll(this.scrollerForScroll) {
        Column() {
          ForEach(this.calenderArr, (item:CalenderType[], index) =&amp;gt; {
            CalenderItem({ arr: item, index: index, calendarMode: this.calendarMode })
          })
        }
      }
      .width("100%")
      .layoutWeight(1)
    }
    .width('100%')
    .layoutWeight(1)
  }
}

@ComponentV2
struct CalenderItem {
  @Local colorIndex: number = 999
  @Param calendarMode: number = 0;
  @Param arr: CalenderType[] = []
  @Param index: number = 0
  year: number = 0
  month: number = 0

  aboutToAppear(): void {
    this.year = (new Date()).getFullYear();
    this.month = (new Date()).getMonth() + 1 + this.index
  }

  build() {
    Column({ space: 5 }) {
      Text(this.year + 'year' + this.month + 'month')
        .fontSize(18)
        .width('100%')
        .padding(8)
        .textAlign(TextAlign.Center)
      //   dividingLine
      Divider()
        .color(Color.Red)
        .strokeWidth(2)
        .margin({ top: 1, bottom: 10 })
      Grid() {
        ForEach(this.arr, (item: CalenderType, index: number) =&amp;gt; {
          GridItem() {
            Column() {
              Text(item.Calendar)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .fontColor(item.fontColor)
              Text(item.Lunar)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .fontColor(item.fontColor)
            }

            // .margin(15)
          }
          .layoutWeight(1)
          .aspectRatio(1) //'#9DC3E6'
          .backgroundColor(item.Color)
          .onClick(() =&amp;gt; {
            if (item.index == -1) {
              return
            }
            if (this.calendarMode == 0) {
              item.fontColor = Color.White
              item.Color = '#ff2578cf'
              this.arr[index] = new CalenderTypeModel(item)
              // Deposit a single point of data
              AppStorage.setOrCreate&amp;lt;Date&amp;gt;('', item.Time)
              return
            }
          })
        })
      }
      .rowsGap(10)
      .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Explanation: This time, the single-point selection function of the calendar has been implemented and a comprehensive state management upgrade has been carried out, and the core optimization includes five aspects: first, the introduction of advanced features of ArkUI (@ObservedV2/@ComponentV2) to reconstruct state management, and the creation of observable CalenderTypeModel classes to achieve fine-grained updates; Secondly, the calendar mode switch (calendarMode) is added, which currently implements the single-point selection function of mode 0 - when you click on the date, the background turns blue (#ff2578cf), the text turns white, and the new CalenderTypeModel() triggers a responsive update; Third, integrated routing parameter processing (router.getParams()) supports automatic setting of the mode according to the "single point calendar" parameter; Fourth, enhance the date data structure - add a Time field to store the full date object, and set the blank date to null; Finally, the rendering logic is refactored: the parent component uses ForEach to iterate through the two-dimensional array calenderArr, and the child component CalenderItem receives the mode parameters and implements click interaction, and the expiration date is automatically stored in AppStorage after clicking. The current implementation lays the foundation for the subsequent expansion of the dotted/multi-select mode, while maintaining visual characteristics such as the weekend red logo and the square grid layout. ）&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k3lnqtgv0bh4kjiukkd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k3lnqtgv0bh4kjiukkd.png" alt="Image description" width="301" height="723"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;​&lt;/p&gt;

</description>
      <category>harmonyos</category>
    </item>
    <item>
      <title>HarmonyOS NEXT- project actual combat calendar, novice tutorial 2</title>
      <dc:creator>xiaoyaolihua</dc:creator>
      <pubDate>Sun, 29 Jun 2025 14:46:50 +0000</pubDate>
      <link>https://dev.to/xiaoyaolihua/harmonyos-next-project-actual-combat-calendar-novice-tutorial-2-eib</link>
      <guid>https://dev.to/xiaoyaolihua/harmonyos-next-project-actual-combat-calendar-novice-tutorial-2-eib</guid>
      <description>&lt;p&gt;​&lt;br&gt;
In the last article, we finished the basic rendering of the calendar. Next, we began to internationalize the calendar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calendar, successfully completed the acquisition of lunar calendar date.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The sample code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { i18n } from '@kit.LocalizationKit';
@Entry
@Component
struct Index {
  private scrollerForScroll: Scroller = new Scroller();
  private scrollerForList: Scroller = new Scroller();
  @State listPosition: number = 0; // 0 means scrolling to the top of the List, 1 means intermediate value, and 2 means scrolling to the bottom of the List.
  //Design lunar calendar time
  getLunarDate(year: number, month: number, day: number) {
    // 1. Set Gregorian calendar date
    const gregorianCalendar = i18n.getCalendar("zh-Hans", "gregory");
    gregorianCalendar.set(year, month - 1, day); // 月份需减1

    // 2. Convert to lunar calendar
    const lunarCalendar = i18n.getCalendar("zh-Hans", "chinese");
    lunarCalendar.setTime(gregorianCalendar.getTimeInMillis());

    // 3. Get the lunar calendar value
    const lunarYear = lunarCalendar.get("year");
    const lunarMonth = lunarCalendar.get("month");
    const lunarDay = lunarCalendar.get("date");

    // 4. Convert to Chinese  lunar calendar
    const lunarMonths = ['New Year', 'February', 'March', 'April', 'May', 'June',
      'July', 'August', 'September', 'October', 'Winter Moon', 'Wax Moon'];
    const lunarDays = ['Lunar 1st', '2nd Lunar Month', '3rd Lunar Synchronous', '4th Lunar Synchronous', '5th Lunar Attainment', 'Lunar 6', 'Lunar 7', 'Lunar 8', 'Lunar 9', 'Lunar 10',
      'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen', 'Twenty',
      'Twenty-one', 'Twenty-two', 'Twenty-three', 'Twenty-four', 'Twenty-five', 'Twenty-six', 'Twenty-Twenty-Seven', 'Twenty-Eight', 'Twenty-Nine', 'Thirty'];
  }


  getTime(index: number = 0) {
    let arr: string[] = []
    const date = new Date();
    const year = date.getFullYear();
    const month = date.getMonth() + 1 + index;
    const day = date.getDate();
    const week = date.getDay();
    // Example: Get the day of the week on Thursday, June 5, 2025
    const targetDate = new Date(year, month - 1, 1); // Note: Months are counted from 0 (5 for June)
    const dayIndex = targetDate.getDay(); // Back 4 (Thursday)
    const weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"
      , "Friday", "Saturday"];
    for (let i = 0; i &amp;lt; dayIndex; i++) {
      arr.push('')
    }
    // Get how many days there are in a month
    const days = new Date(year, month, 0).getDate();
    for (let i = 1; i &amp;lt; days + 1; i++) {
      arr.push(i + '')
    }
    return arr
  }

  aboutToAppear(): void {
    this.getTime()
    this.getLunarDate(2025, 6, 7)
  }

  build() {
    Column() {
      // Nav head navigation bar
      Row() {
        Image($r('sys.media.ohos_ic_public_arrow_left'))
          .width(28)
          .aspectRatio(1)
          .fillColor(Color.Black)
          .onClick(() =&amp;gt; {

          })
        Text('Calendar')
          .fontSize(18)
        Column()
          .width(28)
      }
      .padding(8)
      .width('100%')
      .backgroundColor('#50cccccc')
      .justifyContent(FlexAlign.SpaceBetween)

      // Date arrangement
      Row() {
        Text('Sunday')
          .fontColor('#FF4A24')
        Text('Monday')
        Text('Tuesday')
        Text('Wednesday')
        Text('Thursday')
        Text('Friday')
        Text('Saturday')
          .fontColor('#FF4A24')
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
      .padding(8)

      //Scroll through the calendar
      Scroll(this.scrollerForScroll) {
        Column() {
          CalenderComp({ arr: this.getTime(), index: 0 })
          CalenderComp({ arr: this.getTime(1), index: 1 })
          CalenderComp({ arr: this.getTime(2), index: 2 })
        }
      }
      .width("100%")
      .layoutWeight(1)
    }
    .width('100%')
    .layoutWeight(1)
  }
}

@Component
struct CalenderComp {
  @Prop arr: string[] = []
  @Prop index: number = 0
  year: number = 0
  month: number = 0

  aboutToAppear(): void {
    this.year = (new Date()).getFullYear();
    this.month = (new Date()).getMonth() + 1 + this.index
  }

  build() {
    Column({ space: 5 }) {
      Text(this.year + 'year' + this.month + 'month')
        .fontSize(18)
        .width('100%')
        .padding(8)
        .textAlign(TextAlign.Center)
      //   dividingLine
      Divider()
        .color(Color.Red)
        .strokeWidth(2)
        .margin({ top: 1, bottom: 10 })
      Grid() {
        ForEach(this.arr, (item: number) =&amp;gt; {
          GridItem() {
            Text(item.toString())
              .fontSize(16)
              .textAlign(TextAlign.Center)
          }
        })
      }
      .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Explanation: This time the lunar calendar date conversion function has been added and internationalization support has been introduced, and the core optimization includes three aspects: First, the getLunarDate() method has been added to use &lt;a class="mentioned-user" href="https://dev.to/kit"&gt;@kit&lt;/a&gt;. LocalizationKit's calendar service, which realizes the exact conversion of the Gregorian calendar to the lunar calendar through i18n.getCalendar (supports "zh-Hans" Chinese Simplified Chinese environment); Secondly, the lunar calendar data processing process was improved - the Gregorian calendar date object was set first, then converted to the lunar calendar and the year/month/day data was extracted, and an array of Chinese names of the lunar month and date (such as "first month" and "first two days") was created. Finally, test calling the lunar calendar conversion method in the aboutToAppear lifecycle (example: June 7, 2025). Although the current implementation does not display lunar calendar data in the UI, it lays the foundation for subsequent calendar components to display lunar calendar information, while maintaining the original multi-month Gregorian calendar display function (three consecutive CalenderComp components). The introduction of the internationalization module also creates the conditions for multilingual support. ） &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fel48d78um6gx7dnsvve1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fel48d78um6gx7dnsvve1.png" alt="Image description" width="370" height="777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calendar, the interface shows the lunar calendar.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The sample code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { i18n } from '@kit.LocalizationKit';


interface CalenderType {
  Calendar?: string;
  Lunar?: string;
}
@Entry
@Component
struct Index {
  private scrollerForScroll: Scroller = new Scroller();
  private scrollerForList: Scroller = new Scroller();
  @State listPosition: number = 0; // 0 means scrolling to the top of the List, 1 means intermediate value, and 2 means scrolling to the bottom of the List.
  //Design lunar calendar time
  getLunarDate(year: number, month: number, day: number) {
    // 1. Set Gregorian calendar date
    const gregorianCalendar = i18n.getCalendar("zh-Hans", "gregory");
    gregorianCalendar.set(year, month - 1, day); // 月份需减1

    // 2. Convert to lunar calendar
    const lunarCalendar = i18n.getCalendar("zh-Hans", "chinese");
    lunarCalendar.setTime(gregorianCalendar.getTimeInMillis());

    // 3. Get the lunar calendar value
    const lunarYear = lunarCalendar.get("year");
    const lunarMonth = lunarCalendar.get("month");
    const lunarDay = lunarCalendar.get("date");

    // 4. Convert to Chinese  lunar calendar
    const lunarMonths = ['New Year', 'February', 'March', 'April', 'May', 'June',
      'July', 'August', 'September', 'October', 'Winter Moon', 'Wax Moon'];
    const lunarDays = ['Lunar 1st', '2nd Lunar Month', '3rd Lunar Synchronous', '4th Lunar Synchronous', '5th Lunar Attainment', 'Lunar 6', 'Lunar 7', 'Lunar 8', 'Lunar 9', 'Lunar 10',
      'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen', 'Twenty',
      'Twenty-one', 'Twenty-two', 'Twenty-three', 'Twenty-four', 'Twenty-five', 'Twenty-six', 'Twenty-Twenty-Seven', 'Twenty-Eight', 'Twenty-Nine', 'Thirty'];
    return lunarDays[lunarDay - 1]
  }


  getTime(index: number = 0) {
    let arr: CalenderType[] = []
    const date = new Date();
    const year = date.getFullYear();
    const month = date.getMonth() + 1 + index;
    const day = date.getDate();
    const week = date.getDay();
    // Example: Get the day of the week on Thursday, June 5, 2025
    const targetDate = new Date(year, month - 1, 1); // Note: Months are counted from 0 (5 for June)
    const dayIndex = targetDate.getDay(); // Back 4 (Thursday)
    const weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"
      , "Friday", "Saturday"];
    for (let i = 0; i &amp;lt; dayIndex; i++) {
      arr.push({  })
    }
    // Get how many days there are in a month
    const days = new Date(year, month, 0).getDate();
    for (let i = 1; i &amp;lt; days + 1; i++) {
      arr.push({ Calendar: i + '', Lunar: this.getLunarDate(year, month, i) })

    }
    return arr
  }

  aboutToAppear(): void {
    this.getTime()
    this.getLunarDate(2025, 6, 7)
  }

  build() {
    Column() {
      // Nav head navigation bar
      Row() {
        Image($r('sys.media.ohos_ic_public_arrow_left'))
          .width(28)
          .aspectRatio(1)
          .fillColor(Color.Black)
          .onClick(() =&amp;gt; {

          })
        Text('Calendar')
          .fontSize(18)
        Column()
          .width(28)
      }
      .padding(8)
      .width('100%')
      .backgroundColor('#50cccccc')
      .justifyContent(FlexAlign.SpaceBetween)

      // Date arrangement
      Row() {
        Text('Sunday')
          .fontColor('#FF4A24')
          .layoutWeight(1)
          .fontSize(12)
        Text('Monday')
          .layoutWeight(1)
          .fontSize(12)

        Text('Tuesday')
          .layoutWeight(1)
          .fontSize(12)

        Text('Wednesday')
          .layoutWeight(1)
          .fontSize(12)
        Text('Thursday')
          .fontSize(12)
          .layoutWeight(1)
        Text('Friday')
          .fontSize(12)
          .layoutWeight(1)
        Text('Saturday')
          .fontSize(12)
          .layoutWeight(1)
          .fontColor('#FF4A24')
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
      .padding(8)

      //Scroll through the calendar
      Scroll(this.scrollerForScroll) {
        Column() {
          CalenderComp({ arr: this.getTime(), index: 0 })
          CalenderComp({ arr: this.getTime(1), index: 1 })
          CalenderComp({ arr: this.getTime(2), index: 2 })
        }
      }
      .width("100%")
      .layoutWeight(1)
    }
    .width('100%')
    .layoutWeight(1)
  }
}

@Component
struct CalenderComp {
  @Prop arr: CalenderType[] = []
  @Prop index: number = 0
  year: number = 0
  month: number = 0

  aboutToAppear(): void {
    this.year = (new Date()).getFullYear();
    this.month = (new Date()).getMonth() + 1 + this.index
  }

  build() {
    Column({ space: 5 }) {
      Text(this.year + 'year' + this.month + 'month')
        .fontSize(18)
        .width('100%')
        .padding(8)
        .textAlign(TextAlign.Center)
      //   dividingLine
      Divider()
        .color(Color.Red)
        .strokeWidth(2)
        .margin({ top: 1, bottom: 10 })
      Grid() {
        ForEach(this.arr, (item: CalenderType) =&amp;gt; {
          GridItem() {
            Column() {
              Text(item.Calendar)
                .fontSize(16)
                .textAlign(TextAlign.Center)
              Text(item.Lunar)
                .fontSize(16)
                .textAlign(TextAlign.Center)
            }
          }
        })
      }
      .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Explanation: This time, the calendar function of dual line display of Gregorian calendar and lunar calendar has been realized, and the core optimization includes four aspects: first, the CalenderType interface is introduced to define a composite data structure, including the Gregorian calendar (Calendar) and lunar calendar (Lunar) fields; Secondly, the getTime() method is reconstructed to dynamically add the corresponding lunar text (such as "the third day of the first month") for each Gregorian calendar date when generating the date array, and the blank space at the beginning of the month is occupied by an empty object. Thirdly, the calendar component is upgraded to a vertical two-line layout (Column nesting), which displays both Gregorian numbers and lunar text, maintaining the same text style (centered/16 characters); Finally, optimize the week header line - add layoutWeight(1) to all text to achieve monospace, uniform font size of 12 and keep the red logo of Sunday/Saturday. At present, the complete display of the Chinese and Western calendars is realized, and the Gregorian calendar is on the top and the lunar calendar is on the bottom in each date grid, laying the foundation for subsequent festival markers and other functions. ）&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lvbc7kgfyckp5f0jjo5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lvbc7kgfyckp5f0jjo5.png" alt="Image description" width="385" height="791"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;​&lt;/p&gt;

</description>
      <category>harmonyos</category>
    </item>
    <item>
      <title>HarmonyOS Next- project actual combat, calendar development novice tutorial 1</title>
      <dc:creator>xiaoyaolihua</dc:creator>
      <pubDate>Sun, 29 Jun 2025 14:45:36 +0000</pubDate>
      <link>https://dev.to/xiaoyaolihua/harmonyos-next-project-actual-combat-calendar-development-novice-tutorial-1-1305</link>
      <guid>https://dev.to/xiaoyaolihua/harmonyos-next-project-actual-combat-calendar-development-novice-tutorial-1-1305</guid>
      <description>&lt;p&gt;​&lt;br&gt;
The author, began to lead everyone from scratch, our calendar actual combat.&lt;/p&gt;

&lt;p&gt;The first step is to complete the calendar header navigation bar of our calendar.&lt;/p&gt;

&lt;p&gt;The sample code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entry
struct Index {

  build() {
    Column() {
      Row() {
        Image($r('sys.media.ohos_ic_public_arrow_left'))
          .width(24)
          .aspectRatio(1)
          .fillColor(Color.Black)
          .onClick(() =&amp;gt; {
          })
        Text('calendar')
        Column()
          .width(24)
      }
      .padding(5)
      .width('100%')
      .backgroundColor('#50cccccc')
      .justifyContent(FlexAlign.SpaceBetween)

      Text('123321')
    }
    .width('100%')
    .height('100%')
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Explain: Adopt a full-screen vertical layout (Column). At the top is a horizontal navigation bar (Row) with three elements: a 24x24 black return arrow icon (referencing system resources ohos_ic_public_arrow_left) on the left, a "calendar" header text in the middle, and a 24-width placeholder space on the right. The navigation bar has 5 units of padding, a translucent gray background (#50cccccc), and a symmetrical distribution of left and right elements through a two-end alignment layout (SpaceBetween). The return icon has an empty click event callback (the actual return logic needs to be supplemented). The main body area of the page currently only displays the "123321" text, which should be replaced with the calendar function component in practice. The entire page is set to 100% width and height to fill the screen. ）&lt;/p&gt;

&lt;p&gt;​​​​​​&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrdfvsivbtyu09mo1les.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrdfvsivbtyu09mo1les.png" alt="Image description" width="274" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2: Calendar, which completes the logic of obtaining calendar date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entry
@Component
struct Index {
  private scrollerForScroll: Scroller = new Scroller();
  private scrollerForList: Scroller = new Scroller();
  @State listPosition: number = 0; // 0 means scrolling to the top of the List, 1 means intermediate value, and 2 means scrolling to the bottom of the List.
  private arr: | string[] = [];

  getTime(index: number = 1) {
    const date = new Date();
    const year = date.getFullYear();
    const month = date.getMonth() + 1 + index;
    const day = date.getDate();
    const week = date.getDay();
    // Example: Get the day of the week on Thursday, June 5, 2025
    const targetDate = new Date(year, month - 1, 1); // Note: Months are counted from 0 (5 for June)
    const dayIndex = targetDate.getDay(); // Back 4 (Thursday)
    const weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"
      , "Friday", "Saturday"];
    for (let i = 0; i &amp;lt; dayIndex; i++) {
      this.arr.push('')
    }
    // Get how many days there are in a month
    const days = new Date(year, month, 0).getDate();
    for (let i = 1; i &amp;lt; days + 1; i++) {
      this.arr.push(i + '')
    }
  }

  aboutToAppear(): void {
    this.getTime()
  }

  build() {
    Column() {
      // Nav head navigation bar
      Row() {
        Image($r('sys.media.ohos_ic_public_arrow_left'))
          .width(28)
          .aspectRatio(1)
          .fillColor(Color.Black)
          .onClick(() =&amp;gt; {

          })
        Text('Calendar')
          .fontSize(18)
        Column()
          .width(28)
      }
      .padding(8)
      .width('100%')
      .backgroundColor('#50cccccc')
      .justifyContent(FlexAlign.SpaceBetween)

      // Date arrangement
      Row() {
        Text('Sunday')
          .fontColor('#FF4A24')
        Text('Monday')
        Text('Tuesday')
        Text('Wednesday')
        Text('Thursday')
        Text('Friday')
        Text('Saturday')
          .fontColor('#FF4A24')
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
      .padding(8)

      //Scroll through the calendar
      Scroll(this.scrollerForScroll) {
        Column() {
          Column() {
            Text('June 2025')
              .fontSize(18)
              .width('100%')
              .padding(8)
              .textAlign(TextAlign.Center)
            //   Dividing line
            Divider()
              .color('#CCC')
            Grid() {
              ForEach(this.arr, (item: number) =&amp;gt; {
                GridItem() {
                  Text(item.toString())
                    .fontSize(16)
                    .textAlign(TextAlign.Center)
                }
              })
            }
            .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
          }
        }
      }
      .width("100%")
      .layoutWeight(1)
    }
    .width('100%')
    .layoutWeight(1)

  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Explanation: This improved code implements a complete calendar function on the basis of the original navigation framework, and mainly includes three core optimizations: first, the new date processing logic (getTime method) dynamically generates calendar data, and initializes the date array for June 2025 (including the blank placeholder at the beginning of the month and the actual date) on page load through the aboutToAppear lifecycle; Secondly, the interface is refactored into a three-part structure - the top navigation bar (increase the size of the icon to 28), the middle day of the week header row (marked in red for Sunday/Saturday), and the scrollable calendar area at the bottom (using the Scroller controller); Finally, the 7-column grid layout (Grid) is used to display the date, the date data is dynamically rendered through ForEach, and the month title "2025-06" and a gray dividing line are added. The current implementation completely displays the static calendar, and functions such as month switching, daily highlighting, and return logic need to be added in the future. ）&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftluowpge720qhf3t8y8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftluowpge720qhf3t8y8w.png" alt="Image description" width="375" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3: Calendar basic module completes rendering.&lt;/p&gt;

&lt;p&gt;The sample code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entry
@Component
struct Index {
  private scrollerForScroll: Scroller = new Scroller();
  private scrollerForList: Scroller = new Scroller();
  @State listPosition: number = 0; // 0 means scrolling to the top of the List, 1 means intermediate value, and 2 means scrolling to the bottom of the List.

  getTime(index: number = 0) {
    let arr: string[] = []
    const date = new Date();
    const year = date.getFullYear();
    const month = date.getMonth() + 1 + index;
    const day = date.getDate();
    const week = date.getDay();
    // Example: Get the day of the week on Thursday, June 5, 2025
    const targetDate = new Date(year, month - 1, 1); // Note: Months are counted from 0 (5 for June)
    const dayIndex = targetDate.getDay(); // Back 4 (Thursday)
    const weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"
      , "Friday", "Saturday"];
    for (let i = 0; i &amp;lt; dayIndex; i++) {
      arr.push('')
    }
    // Get how many days there are in a month
    const days = new Date(year, month, 0).getDate();
    for (let i = 1; i &amp;lt; days + 1; i++) {
      arr.push(i + '')
    }
    return arr
  }

  aboutToAppear(): void {
    this.getTime()
  }

  build() {
    Column() {
      // Nav head navigation bar
      Row() {
        Image($r('sys.media.ohos_ic_public_arrow_left'))
          .width(28)
          .aspectRatio(1)
          .fillColor(Color.Black)
          .onClick(() =&amp;gt; {

          })
        Text('Calendar')
          .fontSize(18)
        Column()
          .width(28)
      }
      .padding(8)
      .width('100%')
      .backgroundColor('#50cccccc')
      .justifyContent(FlexAlign.SpaceBetween)

      // Date arrangement
      Row() {
        Text('Sunday')
          .fontColor('#FF4A24')
        Text('Monday')
        Text('Tuesday')
        Text('Wednesday')
        Text('Thursday')
        Text('Friday')
        Text('Saturday')
          .fontColor('#FF4A24')
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
      .padding(8)

      //Scroll through the calendar
      Scroll(this.scrollerForScroll) {
        Column() {
          CalenderComp({ arr: this.getTime(), index: 0 })
          CalenderComp({ arr: this.getTime(1), index: 1 })
          CalenderComp({ arr: this.getTime(2), index: 2 })
        }
      }
      .width("100%")
      .layoutWeight(1)
    }
    .width('100%')
    .layoutWeight(1)
  }
}

@Component
struct CalenderComp {
  @Prop arr: string[] = []
  @Prop index: number = 0
  year: number = 0
  month: number = 0

  aboutToAppear(): void {
    this.year = (new Date()).getFullYear();
    this.month = (new Date()).getMonth() + 1 + this.index
  }

  build() {
    Column({ space: 5 }) {
      Text(this.year + 'year' + this.month + 'month')
        .fontSize(18)
        .width('100%')
        .padding(8)
        .textAlign(TextAlign.Center)
      //   dividingLine
      Divider()
        .color(Color.Red)
        .strokeWidth(2)
        .margin({ top: 1, bottom: 10 })
      Grid() {
        ForEach(this.arr, (item: number) =&amp;gt; {
          GridItem() {
            Text(item.toString())
              .fontSize(16)
              .textAlign(TextAlign.Center)
          }
        })
      }
      .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Explanation: This code realizes the multi-month calendar display function through componentized refactoring, and the core optimization includes three aspects: first, the calendar display logic is extracted into an independent component, CalenderComp, and the component is reused by receiving the date array and month offset parameters by &lt;a class="mentioned-user" href="https://dev.to/prop"&gt;@prop&lt;/a&gt;; Secondly, the getTime() method is refactored to return the date array instead of modifying the member variables, and supports the dynamic generation of calendar data for three consecutive months (the current month and the next two months). Finally, the parent component displays a multi-month view in a scrolling container nested with three calendar components. Each calendar component dynamically calculates the year-month header (e.g., "2025year6month"), distinguishes the month with a red bold dividing line, and maintains a 7-column grid layout to display the date. The component-based design significantly improves code reusability and maintainability, laying the foundation for subsequent functional expansion. ）&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc80rbn6effllzy155sb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc80rbn6effllzy155sb.png" alt="Image description" width="314" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;​&lt;/p&gt;

</description>
      <category>harmonyos</category>
    </item>
  </channel>
</rss>
