Переглянути джерело

feat: 课程表的UI和相应的交互

YuJing 1 місяць тому
батько
коміт
06beb2c5d2

+ 2 - 2
features/feature/src/main/ets/model/Index.ets

@@ -1,7 +1,7 @@
 // 撤销数据
 @ObservedV2
 export class undoData {
-  student?: string[]
-  seatingPlan?: Array<string[]>
+  arr?: string[]
+  arr2?: Array<string[]>
   columnMode?: number
 }

+ 123 - 0
features/feature/src/main/ets/pages/ClassSchedulePage.ets

@@ -0,0 +1,123 @@
+import { BasicType } from 'basic';
+import { ClassSchedulePageViewModel } from '../viewModel/ClassSchedulePageViewModel';
+
+@ComponentV2
+struct ClassSchedulePage {
+  @Local vm: ClassSchedulePageViewModel = new ClassSchedulePageViewModel(this.getUIContext());
+
+  build() {
+    NavDestination() {
+      Column({space: 10}){
+        // 功能控制按钮
+        Row(){
+          ForEach(this.vm.controlBtn, (item: BasicType, index) => {
+            this.controlButton(item.text!, item.click!)
+          })
+        }
+        .height(30)
+        .width("100%")
+        .padding({left:128})
+        .justifyContent(FlexAlign.SpaceBetween)
+
+        Scroll(){
+          Row(){
+            // 周
+            Column({space: 30}){
+              ForEach(this.vm.weekList, (item: string, index) => {
+                Row(){
+                  Text(item)
+                    .fontSize(16)
+                    .fontWeight(500)
+                    .fontColor('#333333')
+                }
+                .width(80)
+                .height(30)
+                .borderRadius(10)
+                .backgroundColor(Color.White)
+                .alignItems(VerticalAlign.Center)
+                .justifyContent(FlexAlign.Center)
+                .border({ width: 1, color: '#7186F9'})
+                .padding({top: 7, bottom: 7})
+                .draggable(true)
+              })
+            }
+            .borderRadius(15)
+            .justifyContent(FlexAlign.Start)
+            .alignItems(HorizontalAlign.Center)
+            .padding({ top: 17, left:25, right: 18, bottom: 6 })
+
+            // 课程表
+            Column(){
+              Scroll(){
+                Column({space: 18.4}){
+                  ForEach(this.vm.table, (seating: string[], x: number) => {
+                    Row({space: 18}){
+                      ForEach(seating, (item: string, y: number) => {
+                        Row(){
+                          TextInput({text: item})
+                            .border(null)
+                            .width("100%")
+                            .textAlign(TextAlign.Center)
+                            .backgroundColor(Color.Transparent)
+                            .onFocus(()=> {
+                              this.vm.onFocus(x, y)
+                            })
+                            .onBlur(() => {
+                              this.vm.onFocus(-1, -1)
+                            })
+                        }
+                        .width(66)
+                        .height(40)
+                        .borderRadius(10)
+                        .backgroundColor('#E7EAFF')
+                        .alignItems(VerticalAlign.Center)
+                        .justifyContent(FlexAlign.Center)
+                        .border({
+                          width: this.vm.selectX == x && this.vm.selectY == y ? 2 : 0,   // 边框宽度
+                          color: '#5668FC',        // 边框颜色
+                          style: BorderStyle.Dashed, // 虚线样式
+                        })
+                      })
+                    }
+                  })
+                }
+              }
+              .width("100%")
+              .align(Alignment.TopStart)
+              .scrollable(ScrollDirection.Horizontal)
+            }
+            .layoutWeight(1)
+            .borderRadius(15)
+            .backgroundColor(Color.White)
+            .padding({left: 15, top: 12, right: 15, bottom: 12})
+          }
+          .alignItems(VerticalAlign.Top)
+          .justifyContent(FlexAlign.Start)
+        }
+        .layoutWeight(1)
+      }
+      .width("100%")
+      .height("100%")
+      .backgroundColor('#E7EAFF')
+      .padding({ top: this.vm.safeTop - 10, right: 15, bottom: 17 })
+    }
+    .hideTitleBar(true)
+    .onBackPressed(() => { return this.vm._onBackPressed() })
+  }
+
+  // 控制按钮
+  @Builder controlButton(text: string, onClick: () => void){
+    Text(text)
+      .fontSize(16)
+      .borderRadius(10)
+      .fontColor(Color.White)
+      .backgroundColor('#7186F9')
+      .padding({left: 8, top: 6, right: 8, bottom: 6})
+      .onClick(onClick)
+  }
+}
+
+@Builder
+function ClassSchedulePageBuilder() {
+  ClassSchedulePage()
+}

+ 2 - 3
features/feature/src/main/ets/pages/SeatingPlanPage.ets

@@ -135,11 +135,10 @@ struct SeatingPlanPage {
       }
       .width("100%")
       .height("100%")
-      .padding({ left: 15, top: 17, right: 15, bottom: 17 })
+      .backgroundColor('#F5F6FF')
+      .padding({ left: 15, top: this.vm.safeTop, right: 15, bottom: 17 })
     }
     .hideTitleBar(true)
-    .backgroundColor('#F5F6FF')
-    .padding({ top: this.vm.safeTop})
     .onBackPressed(() => { return this.vm._onBackPressed() })
   }
 

+ 10 - 0
features/feature/src/main/ets/utils/Index.ets

@@ -0,0 +1,10 @@
+export function throttle(_f: () => void, _delay: number = 200){
+  let last: number = 0;
+  return () => {
+    const now: number = Date.now();
+    if(now - last > _delay){
+      _f();
+      last = now;
+    }
+  }
+}

+ 5 - 0
features/feature/src/main/ets/utils/RouterUtils.ets

@@ -36,6 +36,11 @@ class RouterUtils{
     return yTRouter.getParamByName('IncreaseScorePage').pop() as IncreaseScorePageModel
   }
 
+  // 课程表页面
+  router2CourseTablePage(param: ESObject, back: Callback<PopInfo>){
+    yTRouter.pushPathByName('ClassSchedulePage', param, back)
+  }
+
 
   // DiaLog
   // 录入信息弹窗

+ 212 - 0
features/feature/src/main/ets/viewModel/ClassSchedulePageViewModel.ets

@@ -0,0 +1,212 @@
+import { BasicType, IBestToast, YTAvoid, yTRouter } from "basic"
+import { window } from "@kit.ArkUI"
+import { undoData } from "../model/Index"
+
+@ObservedV2
+export class ClassSchedulePageViewModel{
+  @Trace safeTop: number = 0
+  @Trace table: Array<string[]> = new Array(5).fill(new Array(8).fill('11'))
+  @Trace weekList: string[] = ['周一', '周二', '周三', '周四', '周五'] // ,
+
+  @Trace selectX: number = -1
+  @Trace selectY: number = -1
+
+  conText: UIContext
+  // 控制按钮
+  controlBtn: Array<BasicType> = [
+    {
+      text: '添加行',
+      click: () => {
+        this._onAddRow()
+      }
+    },
+    {
+      text: '添加列',
+      click: () => {
+        this._onAddColumn()
+      }
+    },
+    {
+      text: '删除行',
+      click: () => {
+        this._onDeleteRow()
+      }
+    },
+    {
+      text: '删除列',
+      click: () => {
+        this._onDeleteColumn()
+      }
+    },
+    {
+      text: '撤销',
+      click: () => {
+        this._onUndo()
+      }
+    },
+    {
+      text: '保存',
+      click: () => {
+        this._onSave()
+      }
+    },
+    {
+      text: '返回',
+      click: () => {
+        this._onBackPressed()
+      }
+    }
+  ]
+  // 上次的操作是不是保存
+  isSave: boolean = false
+  // 撤销数据
+  undoDataList: Array<undoData> = []
+  // 原始的周数据
+  originWeekList: string[] = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+
+  constructor(conText: UIContext) {
+    this.safeTop = AppStorage.get(YTAvoid.SAFE_TOP_KEY) as number
+    this.conText = conText
+
+    window.getLastWindow(this.conText.getHostContext())
+      .then(res => {
+        res.setPreferredOrientation(window.Orientation.LANDSCAPE)
+      })
+  }
+
+  // 获得焦点
+  onFocus(x: number, y: number) {
+    this.selectX = x
+    this.selectY = y
+  }
+
+
+  /**
+   * 添加行
+   */
+  _onAddRow() {
+    let x = this.table.length
+    let y = this.table[0].length
+
+    if(x == 7) return
+
+    this._addUndoData()
+    this.weekList.push(this.originWeekList[this.weekList.length])
+    this.table.push(new Array(y).fill(''))
+  }
+
+  /**
+   * 添加列
+   */
+  _onAddColumn() {
+    let x = this.table.length
+    let y = this.table[0].length
+    this._addUndoData()
+    for (let index = 0; index < this.table.length; index++) {
+      let arr = [...this.table[index]]
+      arr.push('')
+      this.table[index] = arr
+    }
+  }
+
+  /**
+   * 删除行
+   */
+  _onDeleteRow() {
+    let x = this.table.length
+    let y = this.table[0].length
+
+    if(x == 5){
+      IBestToast.show('行数不足5行')
+      return
+    }
+    this._addUndoData()
+    this.weekList.pop()
+    this.table.splice(x-1, 1)
+  }
+
+  /**
+   * 删除列
+   */
+  _onDeleteColumn() {
+    let x = this.table.length
+    let y = this.table[0].length
+
+    if(y == 8) {
+      IBestToast.show('列数不足8列')
+      return
+    }
+    this._addUndoData()
+    for (let index = 0; index < x; index++) {
+      let arr = [...this.table[index]]
+      arr.splice(y-1, 1)
+      this.table[index] = arr
+    }
+  }
+
+
+  /**
+   *  撤销操作
+   */
+  _onUndo() {
+    if(this.undoDataList.length == 0) return
+
+    const undoDate: undoData = this.undoDataList.pop()!
+    this.table = [...undoDate.arr2!]
+    this.weekList = [...undoDate.arr!]
+  }
+
+  /**
+   * 保存
+   */
+  _onSave() {
+    if(this.isSave) return
+    this.isSave = true
+  }
+
+  /**
+   * 添加 《撤销》 数据 - 仅保存 10 次操作
+   */
+  _addUndoData() {
+    this.isSave = false
+    const undoData: undoData = {
+      arr2: [...this.table],
+      arr: [...this.weekList],
+    }
+    this.undoDataList.push(undoData)
+    if(this.undoDataList.length == 10) {
+      this.undoDataList.splice(0, 1)
+    }
+  }
+
+  /**
+   * 回正屏幕并返回
+   */
+  _onBack(){
+    window.getLastWindow(this.conText.getHostContext())
+      .then(res => {
+        res.setPreferredOrientation(window.Orientation.UNSPECIFIED)
+        yTRouter.pop('')
+      })
+  }
+
+  /**
+   * 重写的返回逻辑
+   * @returns
+   */
+  _onBackPressed(){
+    if(!this.isSave) {
+      yTRouter.router2DoubleConfirmDiaLog({
+        text: '编辑内容未保存,确定离开吗?',
+        color: '#7186F9'
+      }, (res) => {
+        if(res && res.result == 'true') {
+          this._onBack()
+        }
+      })
+    } else {
+      this._onBack()
+    }
+    return true;
+  }
+}

+ 5 - 3
features/feature/src/main/ets/viewModel/FiveViewModel.ets

@@ -14,14 +14,16 @@ export class FiveViewModel{
 
   // 查看课程表
   _onLookScore() {
+    iRouter.router2CourseTablePage({ name: '课程表1' }, () => {
 
+    })
   }
 
   // 添加课程表
   _onAddTestScore() {
     iRouter.router2InputDialog({
       color: '#5668FC',
-      message: '请输入课程表',
+      message: '请输入课程表名称',
       text: '添加课程表',
       generics: {
         type: 0
@@ -29,7 +31,7 @@ export class FiveViewModel{
     }, (res) => {
       let ans = res.result as InputDiaLogParams
       if(ans) {
-        iRouter.router2IncreaseScorePage({ name: '课程表1' }, () => {
+        iRouter.router2CourseTablePage({ name: '课程表1' }, () => {
 
         })
       }
@@ -52,7 +54,7 @@ export class FiveViewModel{
   _onEditTestScore() {
     iRouter.router2InputDialog({
       color: '#5668FC',
-      message: '请输入课程表',
+      message: '请输入课程表名称',
       text: '编辑课程表',
       generics: {
         name: '课程表1',

+ 4 - 4
features/feature/src/main/ets/viewModel/FourViewModel.ets

@@ -23,8 +23,8 @@ export class FourViewModel{
   _onAddTestScore() {
     iRouter.router2InputDialog({
       color: '#5668FC',
-      message: '请输入成绩单',
-      text: '添加座位表',
+      message: '请输入考试名称',
+      text: '添加考试',
       generics: {
         type: 0
       }
@@ -54,8 +54,8 @@ export class FourViewModel{
   _onEditTestScore() {
     iRouter.router2InputDialog({
       color: '#5668FC',
-      message: '请输入成绩单',
-      text: '编辑座位表',
+      message: '请输入考试名称',
+      text: '编辑考试',
       generics: {
         name: '座位表1',
         startDate: '2025.11.10',

+ 12 - 5
features/feature/src/main/ets/viewModel/IncreaseStudentPage.ets

@@ -1,6 +1,7 @@
-import { YTAvoid, yTRouter } from "basic"
+import { IBestToast, YTAvoid, yTRouter } from "basic"
 import { iRouter } from "../utils/RouterUtils"
 
+// 添加学生信息页面
 @ObservedV2
 export class IncreaseStudentPageViewModel{
   @Trace safeTop: number = 0
@@ -19,7 +20,6 @@ export class IncreaseStudentPageViewModel{
     }
   }
 
-
   // 修改性别
   _onGenderChange(gender: string){
     this.gender = gender
@@ -27,7 +27,14 @@ export class IncreaseStudentPageViewModel{
 
   // 保存
   _onSave(){
-
+    if(!this.name) {
+      IBestToast.show('请输入姓名')
+      return
+    }
+    if(!this.gender) {
+      IBestToast.show('请选择性别')
+      return
+    }
 
     this._onBackPressed(false)
   }
@@ -37,13 +44,13 @@ export class IncreaseStudentPageViewModel{
    * @returns
    */
   _onBackPressed(isDiaLog: boolean = true){
-    if(isDiaLog){
+    if(isDiaLog && (this.name || this.gender)){
       yTRouter.router2DoubleConfirmDiaLog({
         text: '编辑内容未保存,确定离开吗?',
         color: '#7186F9'
       }, (res) => {
         if (res?.result && res.result == 'true') {
-          yTRouter.pop('')
+          yTRouter.pop()
         }
       })
     } else {

+ 4 - 5
features/feature/src/main/ets/viewModel/SeatingPlanPageViewModel.ets

@@ -202,7 +202,6 @@ export class SeatingPlanPageViewModel{
    *  单、双列切换
    */
   _onChangeColumn() {
-    // todo 在切换的时候判断一下列数是否可以足够显示 双列, 如果不行则在后方补齐
     let x = this.seatingPlan[0].length
     if(x%2 == 1 && this.columnMode == 1) this._onAddColumn()
     this._addUndoData()
@@ -216,8 +215,8 @@ export class SeatingPlanPageViewModel{
     if(this.undoDataList.length == 0) return
 
     const undoDate: undoData = this.undoDataList.pop()!
-    this.seatingPlan = [...undoDate.seatingPlan!]
-    this.dataSource = [...undoDate.student!]
+    this.seatingPlan = [...undoDate.arr2!]
+    this.dataSource = [...undoDate.arr!]
     this.columnMode = undoDate.columnMode!
   }
 
@@ -235,9 +234,9 @@ export class SeatingPlanPageViewModel{
   _addUndoData() {
     this.isSave = false
     const undoData: undoData = {
-      seatingPlan: [...this.seatingPlan],
+      arr2: [...this.seatingPlan],
       columnMode: this.columnMode,
-      student: [...this.dataSource],
+      arr: [...this.dataSource],
     }
     this.undoDataList.push(undoData)
     if(this.undoDataList.length == 10) {

+ 4 - 0
features/feature/src/main/resources/base/profile/router_map.json

@@ -28,6 +28,10 @@
       "name": "IncreaseScorePage",
       "pageSourceFile": "src/main/ets/pages/IncreaseScorePage.ets",
       "buildFunction": "IncreaseScorePageBuilder"
+    }, {
+      "name": "ClassSchedulePage",
+      "pageSourceFile": "src/main/ets/pages/ClassSchedulePage.ets",
+      "buildFunction": "ClassSchedulePageBuilder"
     }
   ]
 }

+ 1 - 1
products/entry/src/main/ets/pages/Index.ets

@@ -54,7 +54,7 @@ struct Index {
   build() {
     Navigation(yTRouter) {
       Column() {
-        Tabs({ controller: this.tabsController, index: 1 }) {
+        Tabs({ controller: this.tabsController }) {
           ForEach(this.contentList, (_: BasicType<undefined>, index) => {
             TabContent() {
               if (index == 0) {