瀏覽代碼

feat: 完成 SecondView.ets 下的UI和逻辑交互

YuJing 1 月之前
父節點
當前提交
3ebf2b13db

二進制
commons/basic/src/main/resources/base/media/icon_expension.png


+ 13 - 10
features/feature/src/main/ets/components/DeerSearch.ets

@@ -5,6 +5,7 @@ import { CustomTextStyle } from "../style/CustomTextStyle"
 @ComponentV2
 export struct DeerSearch {
   @Param enable: boolean = true
+  @Param showSearchButton: boolean = true
 
   @Event onSearch: (text: string) => void
   @Event onChange: (text: string) => void
@@ -49,16 +50,18 @@ export struct DeerSearch {
       .border({width: 2, color: '#000000'})
       .padding({left: 13, right: 13, top: 3, bottom: 3})
 
-      Text('搜索')
-        .borderRadius(31)
-        .backgroundColor('#FECF2F')
-        .border({ width: 2, color: '#000000' })
-        .padding({left: 18, right: 18, top: 9, bottom: 9})
-        .attributeModifier(new CustomTextStyle({size: 12, weight: 600}))
-        .onClick(() => {
-          if(this.enable) this.onSubmit()
-          else this.Touch()
-        })
+      if(this.showSearchButton){
+        Text('搜索')
+          .borderRadius(31)
+          .backgroundColor('#FECF2F')
+          .border({ width: 2, color: '#000000' })
+          .padding({left: 18, right: 18, top: 9, bottom: 9})
+          .attributeModifier(new CustomTextStyle({size: 12, weight: 600}))
+          .onClick(() => {
+            if(this.enable) this.onSubmit()
+            else this.Touch()
+          })
+      }
     }
     .width("100%")
     .onClick(() => {

+ 1 - 1
features/feature/src/main/ets/view/MainView.ets

@@ -3,7 +3,7 @@ import { tagItemComp } from "../components/BuilderIndex"
 import { DeerSearch } from "../components/DeerSearch"
 import { ytBuildComp } from "../components/ytBuildComp"
 import { CustomTextStyle } from "../style/CustomTextStyle"
-import { MainViModel } from "../viewModel/MainViewModel"
+import { MainViModel } from "./viewModel/MainViewModel"
 
 @ComponentV2
 export struct MainView {

+ 231 - 145
features/feature/src/main/ets/view/SecondView.ets

@@ -1,165 +1,251 @@
-@Component
+import { DeerSearch } from '../components/DeerSearch'
+import { SecondViewModel } from './viewModel/SecondViewModel'
+import { CustomTextStyle } from '../style/CustomTextStyle'
+import { bookItemComp } from '../components/BuilderIndex'
+
+@ComponentV2
 export struct SecondView {
-  @State animate_scale: boolean = false; // 商品缩小动画
-  @State toggle: boolean = false; // 轨迹动画控制flag
-  @State imageScaleX: number = 1; // 商品缩小尺寸X
-  @State imageScaleY: number = 1; // 商品缩小尺寸Y
-  @State cartAngle: number = 0; // 购物车抖动幅度
+  vm: SecondViewModel = new SecondViewModel()
 
   build() {
-    Column() {
-      Row() {
-        // 用stack包两个image,实现商品缩小动效;这里需要注意的是,图片给大小,父组件不给大小,跟随image大小;
-        // 使得column包着图片做路径动画;
-        Stack() {
-          Image($r('app.media.app_icon'))
-            .objectFit(ImageFit.Contain)
-            .width(400)
-            .height(400)
-          if (this.animate_scale) {
-            Column() {
-              Image($r('app.media.app_icon'))
-                .objectFit(ImageFit.Contain)
-                .width(400)
-                .height(400)
-                .scale({ x: this.imageScaleX, y: this.imageScaleY })
-                .motionPath({
-                  path: 'Mstart.x start.y C -400 50, -150 200, -360 1480', // -360 1500是相对于原位置的终点位置
-                  from: 0.0,
-                  to: 1.0,
-                  rotatable: false
-                })
-            }
-            .zIndex(3)
-            .alignItems(this.toggle ? HorizontalAlign.Start : HorizontalAlign.Center)
-          }
+    Column(){
+      Row({space: 12}){
+        Row({space: 6}){
+          Text('年龄选择')
+            .attributeModifier(new CustomTextStyle({size: 14, weight: 600}))
+
+          Image($r('[basic].media.icon_expension'))
+            .width(10)
+            .height(8)
+            .rotate({ angle: this.vm.openAgeDialog ? 180 : 0 })
+            .animation({duration: 200, curve: Curve.Smooth})
         }
-      }
-      .backgroundColor(Color.Blue)
+        .borderRadius(18)
+        .backgroundColor('#FFFECF2F')
+        .border({width: 2, color: '#FF000000'})
+        .padding({left: 15, top: 8, right: 15, bottom: 8})
+        .bindMenu(this.ageSelect())
+        .onClick(() => { if(!this.vm.openAgeDialog) this.vm.openAgeDialog = true })
 
-      Column() {
-        Text('XXX元')
-          .fontSize(20)
-        Text('收货地址:XXX')
-          .fontSize(18)
+        DeerSearch({showSearchButton: false, enable: false})
+          .layoutWeight(1)
       }
-      .width('100%')
-      .margin({ left: 20, top: 20, bottom: 20 })
-      .alignItems(HorizontalAlign.Start)
-
-      Row() {
-        Column() {
-          Image($r('app.media.app_icon'))
-            .width(25)
-            .height(25)
-            .margin(5)
-          Text('首页')
-            .fontSize(12)
-        }
+      .width("100%")
+      .padding({left: 16, right: 16, top: this.vm.safeTop, bottom: 20})
+      .linearGradient( this.vm.openTypeDialog ? {colors: [['#FFF2C0', 0], ['#FFF3C6', 1]]} : undefined)
 
-        Column() {
-          Image($r('app.media.app_icon'))
-            .width(25)
-            .height(25)
-            .margin(5)
-          Text('消息')
-            .fontSize(12)
-        }
+      Column(){
+        RelativeContainer(){
+          // 页面的 Title
+          Column(){
+            Row(){
+              Row(){
+                List({space: 22, scroller: this.vm.categoryControl}){
+                  ForEach(this.vm.categoryList, (item: string, index) => {
+                    ListItem(){
+                      Column(){
+                        Text(item)
+                          .zIndex(99)
+                          .onClick(() => { this.vm.changeCategory(index) })
+                          .attributeModifier(new CustomTextStyle({size: 16, weight: 600, color: this.vm.categoryIndex == index ? '#FF111111' : '#FF777777'}))
 
-        Column() {
-          Image($r('app.media.app_icon'))
-            .width(25)
-            .rotate({
-              angle: this.cartAngle
-            })
-            .height(25)
-            .margin(5)
-          Text('购物车')
-            .fontSize(12)
-        }
+                        if(this.vm.categoryIndex == index){
+                          Image($r('[basic].media.icon_SelectBg'))
+                            .width(45)
+                            .height(16)
+                            .margin({top:-10})
+                            .transition(TransitionEffect.asymmetric(TransitionEffect.move(TransitionEdge.TOP), TransitionEffect.move(TransitionEdge.TOP)).animation({ duration: 100, curve: Curve.Smooth }))
+                        }
+                      }
+                      .alignItems(HorizontalAlign.Center)
+                    }
+                  })
+                }
+                .height(40)
+                .width("100%")
+                .scrollBar(BarState.Off)
+                .listDirection(Axis.Horizontal)
+              }.layoutWeight(1)
 
-        Button('加入购物车')
-          .zIndex(1)
-          .backgroundColor('#ffcdcdcd')
-          .fontColor(Color.Red)
-          .fontSize(15)
-          .width(120)
-          .margin({ left: 20, right: 10 })
-          .onClick(() => {
-            this.animate_scale = true;
-            // 先做商品缩小动效
-            this.animationImageScale();
+              Row(){
+                Image($r('[basic].media.icon_expension'))
+                  .width(10)
+                  .height(8)
+                  .rotate({ angle: this.vm.openTypeDialog ? 180 : 0 })
+                  .animation({duration: 200, curve: Curve.Smooth})
+              }
+              .padding({left: 16, top: 2})
+              .onClick(() => { this.vm.openCategoryDialog() })
+
+            }.alignItems(VerticalAlign.Top)
+
+            Row(){
+              Row(){
+                ForEach(['最新', '最热'], (item: string, index: number) => {
+                  Text(item)
+                    .borderRadius(15)
+                    .padding({left: 12, top: 5, right: 12, bottom: 5})
+                    .attributeModifier(new CustomTextStyle({size: 14, weight: 600}))
+                    .border({width: 2, color: this.vm.sortType == index ? '#FF000000' : Color.Transparent})
+                    .backgroundColor(this.vm.sortType == index ? '#FFFECF2F' : Color.Transparent)
+                    .onClick(() => { this.vm.changeSortType(index) })
+                })
+              }
+
+              Row({space: 16}){
+                Row({space: 6}){
+                  Blank()
+                    .width(14)
+                    .aspectRatio(1)
+                    .borderRadius(2)
+                    .border({width: 2, color: '#FF000000'})
+                    .backgroundColor(this.vm.isRead ? '#FFFECF2F' : Color.White)
+                  Text('未读')
+                    .attributeModifier(new CustomTextStyle({size: 14, weight: 400}))
+                }.onClick(() => { this.vm.changeIsRead() })
+
+                Text(this.vm.showMode ? '封面模式' : '列表模式')
+                  .onClick(() => { this.vm.changeShowMode() })
+                  .attributeModifier(new CustomTextStyle({size: 14, weight: 400}))
+              }
+            }
+            .width("100%")
+            .margin({top: 10})
+            .justifyContent(FlexAlign.SpaceBetween)
+          }
+          .width("100%")
+          .id('classify')
+          .backgroundColor(Color.White)
+          .borderRadius({bottomLeft: 20, bottomRight: 20})
+          .padding({left: 16, right: 16, bottom: 18})
+          .alignRules({
+            left: { anchor: '__container__', align: HorizontalAlign.Start },
+            top: { anchor: '__container__', align: VerticalAlign.Top }
           })
-        Button('购买')
-          .width(120)
-          .zIndex(1)
-          .backgroundColor(Color.Red)
-          .fontColor(Color.White)
-          .fontSize(15)
-          .onClick(() => {
 
+
+          // 书籍列表项
+          Column(){
+            List({space: 12}){
+              ListItem()
+
+              Repeat(this.vm.bookList)
+                .each((item) => {
+                  bookItemComp(item.item, () => {})
+                })
+            }
+            .width("100%")
+            .height("100%")
+            .scrollBar(BarState.Off)
+          }
+          .id('list')
+          .width("100%")
+          .layoutWeight(1)
+          .padding({left: 16, right: 16})
+          .backgroundColor(Color.Transparent)
+          .alignRules({
+            left: { anchor: '__container__', align: HorizontalAlign.Start },
+            top: { anchor: 'classify', align: VerticalAlign.Bottom }
           })
+
+
+          // 分类弹窗
+          if(this.vm.openTypeDialog){
+            Column(){
+              Column(){
+                Row(){
+                  Column(){
+                    Text('选择分类')
+                      .zIndex(99)
+                      .attributeModifier(new CustomTextStyle({size: 16, weight: 600}))
+
+                    Image($r('[basic].media.icon_SelectBg'))
+                      .width(45)
+                      .height(16)
+                      .margin({top:-10})
+                  }
+
+                  Column(){
+                    Image($r('[basic].media.icon_expension'))
+                      .width(10)
+                      .height(8)
+                      .rotate({ angle: this.vm.openTypeDialog ? 180 : 0 })
+                      .animation({duration: 200, curve: Curve.Smooth})
+                  }
+                  .width(50)
+                  .height(24)
+                  .alignItems(HorizontalAlign.End)
+                  .justifyContent(FlexAlign.Center)
+                  .onClick(() => { this.vm.openCategoryDialog() })
+                }
+                .width("100%")
+                .justifyContent(FlexAlign.SpaceBetween)
+
+                Grid(){
+                  ForEach(this.vm.categoryList, (item: string, index: number) => {
+                    GridItem(){
+                      Text(item)
+                        .width('100%')
+                        .borderRadius(18)
+                        .textAlign(TextAlign.Center)
+                        .padding({top: 6, bottom: 6})
+                        .backgroundColor(this.vm.categoryIndex == index ? '#FFFECF2F' : '#FFFFFF')
+                        .border({width: 2, color: this.vm.categoryIndex == index ? '#FF000000' : '#FFFFFF'})
+                        .attributeModifier(new CustomTextStyle({size: 16, weight: 400}))
+                        .onClick(() => {
+                          this.vm.changeCategory(index, true)
+                          this.vm.openCategoryDialog()
+                        })
+                    }
+                  })
+                }
+                .rowsGap(14)
+                .columnsGap(23)
+                .margin({top: 13})
+                .maxCount(Math.abs(this.vm.categoryList.length/3))
+                .columnsTemplate('repeat(3, 1fr)')
+              }
+              .width("100%")
+              .onClick(() => {})
+              .padding({left: 17, right: 17, bottom: 13})
+              .borderRadius({bottomLeft: 16, bottomRight: 16})
+              .linearGradient({colors: [['#FFF3C6', 0], ['#FFF9E4', 1]]})
+              .transition(TransitionEffect.translate({ y: -20 })
+                .animation({ duration: 200, curve: Curve.Friction}))
+            }
+            .width("100%")
+            .height("100%")
+            .backgroundColor('rgba(0, 0, 0, 0.5)')
+            .onClick(() => { this.vm.openCategoryDialog() })
+            .alignRules({
+              left: { anchor: '__container__', align: HorizontalAlign.Start },
+              top: { anchor: '__container__', align: VerticalAlign.Top }
+            })
+          }
+        }
+        .width('100%')
+        .height('100%')
+        .backgroundColor('#F7F9FA')
       }
-      .zIndex(1)
-      .height('20%')
       .width('100%')
+      .layoutWeight(1)
     }
-    .justifyContent(FlexAlign.SpaceBetween)
-    .height('100%')
     .width('100%')
+    .height('100%')
   }
 
-  animationImageShake() {
-    animateTo({
-      duration: 100,
-      curve: Curve.Linear,
-      onFinish: () => {
-        animateTo({
-          duration: 100,
-          curve: Curve.Linear,
-          onFinish: () => {
-            // 最终节点,恢复所有设置
-            this.cartAngle = 0;
-            this.animate_scale = false;
-            this.imageScaleX = 1;
-            this.imageScaleY = 1;
-          }
-        }, () => {
-          this.cartAngle = -30;
-        })
-      }
-    }, () => {
-      this.cartAngle = 30;
-    })
-  }
-
-  animationImageScale() {
-    animateTo({
-      duration: 100,
-      curve: Curve.EaseOut,
-      iterations: 1,
-      playMode: PlayMode.Normal,
-      onFinish: () => {
-        console.info('scale play end')
-        // 做路径动效
-        this.animationImageMove();
-      }
-    }, () => {
-      this.imageScaleX = 0.1
-      this.imageScaleY = 0.1
-    })
-  }
-
-  animationImageMove() {
-    animateTo({
-      duration: 100, curve: Curve.Linear,
-      onFinish: () => {
-        console.info('move play end')
-        // 购物车图标抖动动效;
-        // this.animationImageShake();
-      }
-    }, () => {
-      this.toggle = !this.toggle // 通过this.toggle变化组件的位置
-    })
+  @Builder
+  ageSelect(){
+    Column({space: 17}){
+      ForEach(['0-2岁', '3-6岁', '7-10岁', '10岁+'], (item: string, index) => {
+        Text(item)
+          .attributeModifier(new CustomTextStyle({size: 14, weight: 600}))
+      })
+    }
+    .margin(-4)
+    .borderRadius(20)
+    .border({width: 2, color: '#FF000000'})
+    .padding({left: 32, right: 32, top: 19, bottom: 19})
+    .onDisAppear(() => { this.vm.openAgeDialog = false })
   }
 }

+ 0 - 0
features/feature/src/main/ets/viewModel/MainViewModel.ets → features/feature/src/main/ets/view/viewModel/MainViewModel.ets


+ 55 - 0
features/feature/src/main/ets/view/viewModel/SecondViewModel.ets

@@ -0,0 +1,55 @@
+import { YTAvoid } from "basic"
+
+@ObservedV2
+export class SecondViewModel{
+  @Trace safeTop: number = AppStorage.get(YTAvoid.SAFE_TOP_KEY) as number
+
+  // 分类列表
+  @Trace categoryList: string[] = ['全部', '精品主题', '名家大师', '国际获奖', '经典系列', '畅销榜P', '权威推荐', '月度推荐']
+  // 书单列表
+  @Trace bookList: string[] = ['书单1', '书单2', '书单3', '书单4', '书单5', '书单6', '书单7', '书单8', '书单9', '书单10', '书单11', '书单12', '书单13', '书单14', '书单15', '书单16', '书单17']
+
+  // 分类索引
+  @Trace categoryIndex: number = 0
+  // 排序方式 0 - 最新 1 - 最热
+  @Trace sortType: number = 0
+  // 是否已读
+  @Trace isRead: boolean = false
+  // 显示模式 0 - 列表 1 - 封面
+  @Trace showMode: number = 0
+
+  // 打开年龄选择弹窗
+  @Trace openAgeDialog: boolean = false
+  // 选择类型弹窗是否已打开
+  @Trace openTypeDialog: boolean = false
+
+  categoryControl: Scroller = new Scroller()
+
+  // 更改选中的分类
+  changeCategory(index: number, toIndex: boolean = false) {
+    this.categoryIndex = index
+    if (toIndex) {
+      this.categoryControl.scrollToIndex(index)
+    }
+  }
+
+  // 更改排序方式
+  changeSortType(index: number) {
+    this.sortType = index
+  }
+
+  // 切换已读模式
+  changeIsRead() {
+    this.isRead = !this.isRead
+  }
+
+  // 改变显示模式
+  changeShowMode() {
+    this.showMode = this.showMode == 0 ? 1 : 0
+  }
+
+  // 开启选择分类弹窗
+  openCategoryDialog() {
+    this.openTypeDialog = !this.openTypeDialog
+  }
+}