Ver Fonte

feat:
1. 完成首页外部的基本UI和交互
2. 完成日期选择器、时间选择器的UI效果

YuJing há 2 meses atrás
pai
commit
4961cb6e5b

+ 38 - 7
features/feature/src/main/ets/components/DiaryDatePicker.ets

@@ -1,9 +1,15 @@
+import { font } from "@kit.ArkUI";
 
 // 日期选择器
 @Component
 export struct DiaryDatePicker {
   @State selectedDate: Date = new Date();
 
+  private linearInfo: LinearGradientOptions = {
+    colors: [ ['#B9FD2A', 0.01], ['#F5FD6D', 1] ],
+    angle: 150
+  }
+
   selectDateBack: (date: Date) => void = (date: Date) => {
     console.log("选择的日期" + date.toLocaleString())
   }
@@ -12,10 +18,17 @@ export struct DiaryDatePicker {
     Column({space: 10}){
       Row(){
         Text("最新")
+          .fontSize(18)
+          .fontWeight(700)
           .onClick(() => {
             this.selectedDate = new Date()
           })
-        Text("ok")
+        Text("确认")
+          .fontSize(16)
+          .fontWeight(500)
+          .borderRadius(32)
+          .linearGradient(this.linearInfo)
+          .padding({ left: 16, right: 16, top: 4, bottom: 4})
           .onClick(() => {
             this.selectDateBack(this.selectedDate)
           })
@@ -24,13 +37,31 @@ export struct DiaryDatePicker {
       .justifyContent(FlexAlign.SpaceBetween)
       .padding({ left: 16, right: 16, top: 16, bottom: 16})
 
-      DatePicker({
-        end: new Date(),
-        selected: $$this.selectedDate,
-      })
+
+      Stack({alignContent: Alignment.Center}){
+        Row()
+          .width("100%")
+          .height(55)
+          .borderRadius(8)
+          .linearGradient(this.linearInfo)
+
+        DatePicker({
+          end: new Date(),
+          selected: $$this.selectedDate,
+        })
+          .selectedTextStyle({
+            color: Color.Black,
+          })
+      }
+      .padding({ left: 13, right: 13})
     }
     .width("100%")
-    // .height("100%")
-    .backgroundColor('#EEEFF1')
+    .borderRadius(12)
+    .backgroundColor(Color.White)
+    .shadow({
+      radius: 10,
+      type: ShadowType.COLOR,
+      color: Color.Black
+    })
   }
 }

+ 147 - 0
features/feature/src/main/ets/components/DiaryTimePicker.ets

@@ -0,0 +1,147 @@
+@Component
+export struct DiaryTimePicker {
+  @State selectedHour: number = 12
+  @State selectedMinute: number = 0
+  private hourController: SwiperController = new SwiperController()
+  private minuteController: SwiperController = new SwiperController()
+
+  // 回调函数
+  private onConfirm?: (hour: number, minute: number) => void = (hour: number, minute: number) => {
+    console.log('onConfirm' + ((hour+1)%24) + ':' + ((minute+1)%60))
+  }
+  private onCancel?: () => void
+
+  private iwidth = 160
+  private iheight = 180
+  private linearInfo: LinearGradientOptions = {
+    colors: [ ['#B9FD2A', 0.01], ['#F5FD6D', 1] ],
+    angle: 150
+  }
+
+  build() {
+    Column() {
+
+      Row(){
+        Row(){
+          Image($r('app.media.Diary_cancel'))
+            .width(24)
+            .aspectRatio(1)
+        }
+        .alignItems(VerticalAlign.Center)
+        .justifyContent(FlexAlign.Center)
+        .padding({left: 10, right:10})
+        .borderRadius(36)
+        .backgroundColor('#F5F5F7')
+
+        Row(){
+          Image($r('app.media.Diary_Entry'))
+            .width(24)
+            .aspectRatio(1)
+        }
+        .alignItems(VerticalAlign.Center)
+        .justifyContent(FlexAlign.Center)
+        .padding({left: 10, right:10})
+        .borderRadius(36)
+        .linearGradient(this.linearInfo)
+        .onClick(() => {
+          this.onConfirm?.(this.selectedHour, this.selectedMinute)
+        })
+      }
+      .width("100%")
+      .justifyContent(FlexAlign.SpaceBetween)
+      .padding({ left: 15, right: 15, top: 17})
+
+
+      Stack({alignContent: Alignment.Center}){
+        Row()
+          .height(50)
+          .width("100%")
+          .linearGradient(this.linearInfo)
+
+        // 时间选择区域
+        Row() {
+          // 小时选择器
+          Column() {
+            Swiper(this.hourController) {
+              ForEach(new Array(24).fill(0), (item: number, index: number) => {
+                Text(`${index.toString().padStart(2, '0')}`)
+                  .fontWeight(FontWeight.Bold)
+                  .fontSize(index === (this.selectedHour+1)%24 ? 16 : 14)
+                  .width('100%')
+                  .height(30)
+                  .fontColor(index === (this.selectedHour+1)%24 ? '#212245' : '#757575')
+                  .textAlign(TextAlign.Center)
+                  .alignSelf(ItemAlign.Center)
+                  .borderRadius(8)
+              })
+            }
+            .borderColor(Color.Transparent)
+            .loop(true)
+            .displayCount(3)
+            .curve(Curve.Linear)
+            .indicator(false)
+            .vertical(true)
+            .nextMargin(1)
+            .prevMargin(1)
+            .onChange((index: number) => {
+              this.selectedHour = index
+            })
+            .align(Alignment.Center)  // 关键:确保选中项居中
+          }
+          .borderColor(Color.Transparent)
+          .layoutWeight(1)
+          .height(150)
+
+          // 分隔符
+          Text(':')
+            .fontSize(24)
+            .fontWeight(FontWeight.Bold)
+            .fontColor('#333333')
+            .alignSelf(ItemAlign.Center)
+
+          // 分钟选择器
+          Column() {
+            Swiper(this.minuteController) {
+              ForEach(new Array(60).fill(0), (item: number, index: number) => {
+                Text(`${index.toString().padStart(2, '0')}`)
+                  .fontSize(index === (this.selectedMinute+1)%59 ? 16 : 14)
+                  .fontWeight(FontWeight.Bold)
+                  .fontColor(index === (this.selectedMinute+1)%59 ? '#212245' : '#757575')
+                  .width('100%')
+                  .height(30)
+                  .textAlign(TextAlign.Center)
+                  .alignSelf(ItemAlign.Center)
+                  .borderRadius(8)
+              })
+            }
+            .loop(true)
+            .displayCount(3)
+            .curve(Curve.Linear)
+            .indicator(false)
+            .vertical(true)
+            .nextMargin(1)
+            .prevMargin(1)
+            .onChange((index: number) => {
+              this.selectedMinute = index
+            })
+            .align(Alignment.Center)  // 关键:确保选中项居中
+          }
+          .layoutWeight(1)
+          .height(150)
+        }
+        .width('100%')
+        .padding({ left: 12, right: 12 })
+      }
+    }
+    .width(this.iwidth)
+    // .height(this.iheight)
+    .backgroundColor(Color.White)
+    .borderRadius(20)
+    .shadow({
+      radius: 10,
+      type: ShadowType.COLOR,
+      color: Color.Black
+    })
+    // .padding({ bottom: 22})
+  }
+}

+ 0 - 120
features/feature/src/main/ets/utils/weekUtil.ets

@@ -1,120 +0,0 @@
-import { DateInfo } from "../models/DateInfo";
-
-// 星期中文映射表
-const WEEK_MAP = ['日', '一', '二', '三', '四', '五', '六'];
-
-/**
- * 创建一个日期信息对象
- * @param date 日期对象
- * @returns DateInfo 对象
- */
-function createDateInfo(date: Date): DateInfo {
-  const year = date.getFullYear();
-  const month = date.getMonth() + 1;
-  const day = date.getDate();
-  const week = WEEK_MAP[date.getDay()];
-
-  return {
-    year: year,
-    month: month,
-    day: day,
-    week: week,
-    id: new Date(date)
-  };
-}
-
-/**
- * 生成从指定日期开始向前的连续日期数组
- * @param startDate 起始日期(默认当前日期)
- * @param count 生成的日期数量(默认7天)
- * @returns 日期对象数组
- */
-export function generateForwardDateArray(
-  startDate: Date = new Date(),
-  count: number = 7
-): DateInfo[] {
-  const dateArray: DateInfo[] = [];
-
-  // 复制起始日期,避免修改原对象
-  const currentDate = new Date(startDate);
-
-  for (let i = 0; i < count; i++) {
-    // 添加日期信息到数组
-    dateArray.push(createDateInfo(currentDate));
-
-    // 日期减1天(向前)
-    currentDate.setDate(currentDate.getDate() - 1);
-  }
-
-  return dateArray;
-}
-
-/**
- * 生成从指定日期开始向后的连续日期数组,可以包含今天但不能超过今天
- * @param startDate 起始日期(默认当前日期)
- * @param count 生成的日期数量(默认7天)
- * @returns 日期对象数组,包含今天但不包含超过今天的日期
- */
-export function generateBackwardDateArray(
-  startDate: Date = new Date(),
-  count: number = 7
-): DateInfo[] {
-  const dateArray: DateInfo[] = [];
-
-  // 复制起始日期,避免修改原对象
-  const currentDate = new Date(startDate);
-
-  // 获取今天的日期
-  // 获取今天的日期并设置时间为23:59:59:999,便于比较
-  const today = new Date();
-  today.setHours(0, 0, 0, 0);
-  today.setDate(today.getDate() + 1);
-  today.setTime(today.getTime() - 1); // 设置为今天的最后一毫秒
-
-  for (let i = 0; i < count; i++) {
-    // 检查当前日期是否超过今天
-    if (currentDate > today) {
-      break;
-    }
-
-    // 添加日期信息到数组
-    dateArray.push(createDateInfo(currentDate));
-
-    // 日期加1天(向后)
-    currentDate.setDate(currentDate.getDate() + 1);
-  }
-
-  return dateArray;
-}
-
-
-
-/**
- * 生成指定日期前后指定天数的完整日期数组
- * @param centerDate 中心日期
- * @param days 每侧天数(默认30天)
- * @returns 包含前后日期的完整数组,按时间顺序排列
- */
-export function generateCenteredDateArray(
-  centerDate: Date = new Date(),
-  days: number = 30
-): DateInfo[] {
-  // 创建前一天的日期作为前向数组的起始日期
-  const forwardStartDate = new Date(centerDate);
-  forwardStartDate.setDate(centerDate.getDate() - 1);
-
-  // 生成前days天的日期数组(需要反转以保证时间顺序)
-  const forwardArray = generateForwardDateArray(
-    forwardStartDate,
-    days
-  ).reverse();
-
-  // 生成后days天的日期数组(包括当天)
-  const backwardArray = generateBackwardDateArray(centerDate, days + 1);
-
-  // 删除backwardArray中的第一天(即centerDate),避免重复
-  backwardArray.shift();
-
-  // 合并数组
-  return [...forwardArray, ...backwardArray];
-}

+ 236 - 33
features/feature/src/main/ets/view/RecodView.ets

@@ -2,10 +2,28 @@ import { YTAvoid } from 'basic'
 import { DateInfo } from '../models/DateInfo'
 import { RecodeViewModel } from '../viewModels/RecodeViewModel'
 import { DiaryDatePicker } from '../components/DiaryDatePicker'
+import { DiaryTimePicker } from '../components/DiaryTimePicker'
 
 /**
  * 记录页面
  */
+
+/**
+ * 日历组件
+ * 1. 懒加载用上
+ * 2. 纵向 swiper, 最右边(今天)作为头
+ *
+ * 加载方式
+ * 1. 通过监听 swiper index 的变化来触发相应逻辑
+ *
+ * * 未通过日历进行跳转日期
+ * 1. 向左滑动时, 如果日期剩余 < 10,则加载下一周 ( index == list.length - 10 )
+ *
+ * * 通过日历进行跳转日期
+ * * *
+ */
+
+// 小 《 《 《 大
 @Component
 export struct RecodView {
   @StorageProp(YTAvoid.SAFE_BOTTOM_KEY) bottom: number = 0
@@ -13,6 +31,13 @@ export struct RecodView {
   // ViewModel
   @State Vm: RecodeViewModel = new RecodeViewModel()
 
+  @State heightList: number[] = []
+
+  private linearInfo: LinearGradientOptions = {
+    colors: [ ['#B9FD2A', 0.01], ['#F5FD6D', 1] ],
+    angle: 150
+  }
+
   aboutToAppear(): void {
     this.Vm.selectedDateChange = this.selectDateChange
   }
@@ -34,39 +59,57 @@ export struct RecodView {
             animateToImmediately({
               duration: 200
             }, () => {
-              this.Vm.showTimePicker = !this.Vm.showTimePicker;
+              if(this.Vm.showDatePicker) {
+                this.Vm.showDatePicker = false
+              } else {
+                this.Vm.showDatePicker = true
+                this.Vm.showTimePicker = -1
+              }
             })
           })
       }
       .width("100%")
+      .height(76)
       .justifyContent(FlexAlign.SpaceBetween)
+      .alignItems(VerticalAlign.Center)
+      .padding({ left: 17, right: 17, bottom: 17, top: 17})
+      .onClick(() => {
+        this.Vm.showTimePicker = -1
+        this.Vm.showDatePicker = false
+      })
 
+      // 主体内容
       Stack({alignContent: Alignment.TopEnd}){
-        /**
-         * 日历组件
-         * 1. 懒加载用上
-         * 2. 纵向 swiper, 最右边(今天)作为头
-         *
-         * 加载方式
-         * 1. 通过监听 swiper index 的变化来触发相应逻辑
-         *
-         * * 未通过日历进行跳转日期
-         * 1. 向左滑动时, 如果日期剩余 < 10,则加载下一周 ( index == list.length - 10 )
-         *
-         * * 通过日历进行跳转日期
-         * * *
-         */
-
-        // 小 《 《 《 大
         Column(){
+          // 日期选择
           Swiper(this.Vm.swiperController){
             LazyForEach(this.Vm.weekLoop, (item: DateInfo, index: number) => {
-              Column() {
-                Text(item.week)
-                Text(`${item.month}-${item.day}`)
+              Column({space: 2}) {
+                Column({space: 15}){
+                  Text(item.week)
+                  // ${item.month}-
+                  Text(`${item.day}`)
+                }
+                .width("100%")
+                .padding({ top: 5, bottom: 5 })
+                .borderRadius(10)
+                .linearGradient(this.Vm.selectedDate === index ? this.linearInfo : null)
+
+                // TODO 表示某天有数据
+                if(true) {
+                  Row()
+                    .width(6)
+                    .height(6)
+                    .borderRadius(3)
+                    .backgroundColor('#BAFE2B')
+                }
               }
-              .border({
-                width: this.Vm.selectedDate === index ? 1 : 0
+              .width(50)
+              .height(80)
+              .padding({
+                left: 7,
+                right: 7,
+                top: 9,
               })
               .onClick(() => {
                 this.Vm.selectedDate = index
@@ -80,32 +123,192 @@ export struct RecodView {
           .onChange((index: number) => {
             this.Vm.swiperOnChange(index)
           })
+
+          // 记录内容主体
+          List({space: 10}) {
+            ListItem()
+              .height(20)
+              .onClick(() => {
+              this.Vm.showTimePicker = -1
+              this.Vm.showDatePicker = false
+            })
+
+            ForEach(new Array(10).fill(0), (item: number, index: number) => {
+              ListItem(){
+                Stack({alignContent: Alignment.TopStart}){
+                  Row({space: 20}){
+                    // 时间 和 分隔线
+                    Column({space: 5}){
+                      Row(){
+                        Text("13:49")
+                          .fontSize(14)
+                          .padding({
+                            top: 5,
+                            left: 8,
+                            right: 8,
+                            bottom: 5
+                          })
+                          .fontWeight(400)
+                          .borderRadius(8)
+                          .fontColor(Color.Black)
+                          .linearGradient(this.linearInfo)
+                      }
+                      .width(54)
+                      .height(30)
+                      .alignItems(VerticalAlign.Center)
+                      .justifyContent(FlexAlign.Center)
+                      .onClick(() => {
+                        if(this.Vm.showTimePicker === index) {
+                          this.Vm.showTimePicker = -1
+                        } else {
+                          this.Vm.showTimePicker = index
+                          this.Vm.showDatePicker = false
+                        }
+                      })
+
+                      // 分隔线
+                      Column(){
+                        Row() {
+                          Row()
+                            .width(7)
+                            .aspectRatio(1)
+                            .borderRadius(4)
+                            .backgroundColor(Color.White)
+                        }
+                        .width(10)
+                        .aspectRatio(1)
+                        .borderRadius(5)
+                        .backgroundColor('#B9FD2A')
+                        .alignItems(VerticalAlign.Center)
+                        .justifyContent(FlexAlign.Center)
+
+                        Row()
+                          .width(3)
+                          .backgroundColor('#E9E9EC')
+                          .layoutWeight(1)
+
+                        Row() {
+                          Row()
+                            .width(7)
+                            .aspectRatio(1)
+                            .borderRadius(4)
+                            .backgroundColor(Color.White)
+                        }
+                        .width(10)
+                        .aspectRatio(1)
+                        .borderRadius(5)
+                        .backgroundColor('#B9FD2A')
+                        .alignItems(VerticalAlign.Center)
+                        .justifyContent(FlexAlign.Center)
+                      }
+                      .width(30)
+                      .height(this.heightList[index] + 15)
+                    }
+                    // .layoutWeight(1)
+
+                    // 标题 和 内容
+                    Column({space: 5}){
+                      Row(){
+                        Text("生活日记")
+                          .fontSize(14)
+                          .fontWeight(600)
+                          .padding({
+                            top: 5,
+                            left: 8,
+                            right: 8,
+                            bottom: 5
+                          })
+                      }
+                      .height(30)
+                      .alignItems(VerticalAlign.Center)
+                      .justifyContent(FlexAlign.Center)
+
+                      // 内容
+                      Column({space: 8}){
+                        Text("内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容")
+                          .fontSize(12)
+                          .fontColor('#212245')
+                          .opacity(0.5)
+
+                        // 图片显示
+                        Scroll(){
+                          Row({space: 5}){
+                            ForEach(new Array(5).fill(0), (item: number, index: number) => {
+                              Image('https://hm-static.ytpm.net/upload/20250820/fa210da5-b364-4469-a6c8-d8a9038af687-1755657677967.jpeg')
+                                .width(100)
+                                .height(150)
+                                .borderRadius(12)
+                            })
+                          }
+                        }
+                        .scrollable(ScrollDirection.Horizontal)
+                        .scrollBar(BarState.Off)
+
+                      }
+                      .alignItems(HorizontalAlign.Start)
+                      .justifyContent(FlexAlign.Start)
+                      .onAreaChange((_o: Area, _n: Area) => {
+                        this.heightList[index] = _n.height as number
+                      })
+
+                      Blank().height(15)
+                    }
+                    .layoutWeight(1)
+                    .alignItems(HorizontalAlign.Start)
+                    .justifyContent(FlexAlign.Start)
+                  }
+                  .onClick(() => {
+                    this.Vm.showTimePicker = -1
+                    this.Vm.showDatePicker = false
+                  })
+
+
+                  // 时间选择器
+                  if(this.Vm.showTimePicker == index) {
+                    DiaryTimePicker()
+                      .offset({
+                        x: 5,
+                        y: 35
+                      })
+                  }
+                }
+              }
+            })
+          }
+          .width("100%")
+          .layoutWeight(1)
+          .padding({ left: 24, right: 24, top: 10})
+          .scrollBar(BarState.Off)
         }
 
-        if(this.Vm.showTimePicker) {
+        // 时间选择器
+        if(this.Vm.showDatePicker) {
           DiaryDatePicker({
             selectedDate: this.Vm.weekLoop.getData(this.Vm.swiperCurrentIndex).id,
             selectDateBack: (date: Date) => {
-              this.Vm.showTimePicker = false
+              this.Vm.showDatePicker = false
               this.Vm.jumpToDate(date)
             }
           })
-            .width("80%")
+            .width(267)
         }
       }
       .width("100%")
       .layoutWeight(1)
-
-      Button("check")
-        .onClick(() => {
-          for (let index = 0; index < this.Vm.weekLoop.totalCount(); index++) {
-            console.log(`${ this.Vm.swiperCurrentIndex == index ? 'ToDay' : '' } ${this.Vm.weekLoop.getData(index).month} 月 ${this.Vm.weekLoop.getData(index).day} 日 `)
-          }
-        })
+      .backgroundColor(Color.White)
+      .borderRadius({
+        topLeft: 20,
+        topRight: 20
+      })
     }
     .width("100%")
     .height("100%")
-    .padding({ top: this.safeTop + 22, left: 16, right: 16 })
+    .padding({ top: this.safeTop + 22})
+    .linearGradient({
+      colors: [ ['#B9FD2A', 0.01], ['#F5FD6D', 1] ],
+      angle: 150
+    })
+
   }
 }
 

+ 17 - 7
features/feature/src/main/ets/viewModels/RecodeViewModel.ets

@@ -1,6 +1,8 @@
+import { IBestToast, yTToast } from 'basic';
+import { DiaryApi } from '../apis/DiaryApi';
 import { BasicDataSource } from '../models/BasicDataSource';
 import { DateInfo } from '../models/DateInfo';
-import { generateBackwardDateArray, generateForwardDateArray } from '../utils/weekUtil';
+import { DateUtils } from '../utils/DateUtils';
 
 export class RecodeViewModel{
   // 懒加载数据源
@@ -8,7 +10,9 @@ export class RecodeViewModel{
   // swiper 控制器
   swiperController: SwiperController = new SwiperController();
   // 是否显示时间选择器
-  showTimePicker: boolean = false
+  showDatePicker: boolean = false
+  // 是否显示时间选择器
+  showTimePicker: number = -1
   // swiper 容器中当前选中的 index
   swiperCurrentIndex: number = 0
   // 日历中被选中的日期
@@ -17,9 +21,12 @@ export class RecodeViewModel{
   // 外部传入, 用于修改日历中选中的日期 ( 直接在本类中修改 UI 无法进行刷新WW )
   selectedDateChange: (index: number) => void = (index: number) => {}
 
+  /**
+   * 构造函数 - 初始化 weekLoop 数据源
+   */
   constructor() {
     // 初始化列表数据
-    const dateList: DateInfo[] = generateForwardDateArray(new Date, 30);
+    const dateList: DateInfo[] = DateUtils.generateForwardDateArray(new Date, 30);
     console.log("初始化数据" + JSON.stringify(dateList))
     this.weekLoop.init(dateList);
   }
@@ -32,7 +39,7 @@ export class RecodeViewModel{
     // 取得列表最后一条数据的 id
     const dateInfo: Date = this.weekLoop.getData(this.weekLoop.totalCount() - 1).id;
     // 向后加载 10 天的数据
-    const dateArray: DateInfo[] = generateForwardDateArray(dateInfo, days)
+    const dateArray: DateInfo[] = DateUtils.generateForwardDateArray(dateInfo, days)
     dateArray.splice(0, 1)
     console.log("向左加载的数据" +  JSON.stringify(dateArray))
 
@@ -48,7 +55,7 @@ export class RecodeViewModel{
    */
   loadBackwardMoreData(days: number, start?: Date): number {
     // 向后加载 10 天的数据
-    const dateArray: DateInfo[] = generateBackwardDateArray(start ?? new Date(), days)
+    const dateArray: DateInfo[] = DateUtils.generateBackwardDateArray(start ?? new Date(), days)
     console.log("向右加载的数据" + JSON.stringify(dateArray))
     dateArray.splice(0, 1)
 
@@ -84,8 +91,8 @@ export class RecodeViewModel{
    * @param date 跳转到的指定日期
    */
   jumpToDate(date: Date) {
-    const backList: DateInfo[] = generateBackwardDateArray(date, 20).reverse()
-    const forwardList: DateInfo[] = generateForwardDateArray(date, 20)
+    const backList: DateInfo[] = DateUtils.generateBackwardDateArray(date, 20).reverse()
+    const forwardList: DateInfo[] = DateUtils.generateForwardDateArray(date, 20)
     console.log("向右加载的数据" + JSON.stringify(backList))
     console.log("向左加载的数据" + JSON.stringify(forwardList))
 
@@ -99,4 +106,7 @@ export class RecodeViewModel{
     this.swiperController.changeIndex(targetIndex)
     this.selectedDateChange(targetIndex)
   }
+
+  // 请求函数
+
 }

BIN
features/feature/src/main/resources/base/media/Diary_Entry.png


BIN
features/feature/src/main/resources/base/media/Diary_cancel.png


BIN
features/feature/src/main/resources/base/media/RecodeView_Bg.png