Harmony OS个人项目——简单记账APP(2)

页面布局实现

一、首页布局

首页具体功能为:提供一个导航栏,分别为明细、账单、添加账目信息、收支统计、我的信息五个图标,分别用于跳转到各自页面进行操作。具体代码如下:

@Entry
@Component
struct Index {

  build() {
    Column() {
      Row(){
        //明细图标:用于跳转到明细详情页
        Column(){
          Image($r('app.media.detailed'))
            .width(50)
          Text('明细')
            .fontSize(15)
        }
        .width(70)
        
        //账单图标:用于跳转到账单页
        Column(){
          Image($r('app.media.bill'))
            .width(45)
          Text('账单')
            .fontSize(15)
        }
        .width(70)
        
        //添加图标:用于添加一笔账目信息
        Column(){
          Button('+')
            .fontSize(60)
            .backgroundColor(Color.Pink)
            .borderColor(Color.Black)
            
        }
        .width(90)
        //存钱图标:用于跳转到存钱页面
        Column(){
          Image($r('app.media.statistics'))
            .width(40)
            .margin({bottom: 7})
          Text('统计')
            .fontSize(15)
            
        }
        .width(70)
        //我的图标:用于跳转到账号管理页面
        Column(){
          Image($r('app.media.mine'))
            .width(40)
          Text('我的')
            .fontSize(15)
        }
        .width(70)
        
      }
      .width('100%')
      .height(100)
      .position({x: 0,y: 690})
    }
    .width('100%')
    .height('100%')
    .backgroundImage($r('app.media.background'))
    .backgroundImageSize({width: 370, height: 750})
  }
}

页面效果展示如下:

二、明细页面布局

该页面用于展示一天的收支的详细情况,首先为标题行,包括返回主页面的按钮以及该页面标题,然后是通过 new Date() 显示当前日期,然后通过List遍历当日所有的收支情况并显示在页面。因为每一条收支情况都包含图片展示、类别以及金额,因此编写了一个类用来存储。具体代码如下:

明细页面布局代码:

import detailedInfo from '../viewmodel/detailedInfo'

@Entry
@Component
struct Detailed {

  //明细
  details: detailedInfo[] = [
    new detailedInfo('购物',$r('app.media.img29'),6.00),
    new detailedInfo('运动',$r('app.media.img28'),10)
  ]

  //日期
  date = new Date();

  build() {
    Column(){
      //标题栏一行:包含返回按钮以及标题
      Row(){
        Image($r('app.media.back'))
          .width(50)
          .borderRadius(50)
        Text('今日账目明细')
          .fontSize(30)
      }
      .width('100%')
      .margin({bottom: 30})
      //显示日期:通过Date的函数获取日期,以年/月/日的形式展示
      Row(){
        Text(this.date.getFullYear()+'/'+this.date.getMonth()+'/'+this.date.getDate())
          .fontSize(20)
      }
      .width('100%')
      .justifyContent(FlexAlign.Start)
      .padding({left: 10, right: 10})
      .margin({bottom: 10})
      //明细列表
      Row(){
        List(){
          ForEach(
            this.details,
            (item: detailedInfo) => {
              ListItem(){
                Row(){
                  Row(){
                    Image(item.img)
                      .width(50)
                      .borderRadius(50)
                    Text(item.name)
                      .fontSize(25)
                      .margin({left: 10})
                  }
                  Text('¥' + item.amount.toFixed(2))
                    .fontSize(25)
                    .fontColor(Color.Red)
                }
                .width('100%')
                .padding({left: 10, right: 10})
                .margin({top: 10})
                .justifyContent(FlexAlign.SpaceBetween)
              }
            }
          )
        }
      }
      .width('90%')
      .backgroundColor(Color.White)
      .borderRadius(20)
      .shadow({radius: 20,color: Color.Black,offsetX: 0,offsetY: 0})
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ffbfe8fd')
  }
}

明细类的设计:

export default class detailedInfo{
  //类别名称
  name: string
  //类别图片
  img: ResourceStr
  //收支金额
  amount: number
  //交易时间
  date: Date

  constructor(name: string, img: ResourceStr, amount: number) {
    this.name = name
    this.img = img
    this.amount = amount
  }
}

注:此类中的date感觉可能会用到,先设计在内,暂时还没有用到。

页面效果展示如下:

三、收支操作页面布局

该页面布局设计思路为:通过点击账目类别,通过弹出框输入收支金额,然后点击确认进行后面的操作。其中账目类别的设计思路为若干行四列的表格,每个类别都包含图片和名称,所以设计一个类来存储。弹出框写入了明细子页面中。具体代码如下:

收支详细页面设计

import classInfo from '../viewmodel/classInfo'
import amountPanel from '../views/amountPanel'

@Entry
@Component
struct addAccounts {
  //图片
  image: ResourceStr
  //名称
  name: string
  //选中类别下标
  index: number = -1
  //显示支出列表
  @State showPayoutList: boolean = true
  //支出颜色
  @State payoutColor: string = '#ffff0000'
  //收入颜色
  @State incomeColor: string = '#ff000000'
  //支出选项
  payout: classInfo[] = [
      new classInfo('餐饮',$r('app.media.img1')),
      new classInfo('购物',$r('app.media.img2')),
      new classInfo('日用',$r('app.media.img3')),
      new classInfo('交通',$r('app.media.img4')),
      new classInfo('零食',$r('app.media.img5')),
      new classInfo('运动',$r('app.media.img6')),
      new classInfo('娱乐',$r('app.media.img7')),
      new classInfo('通讯',$r('app.media.img8')),
      new classInfo('服饰',$r('app.media.img9')),
      new classInfo('美容',$r('app.media.img10')),
      new classInfo('住房',$r('app.media.img11')),
      new classInfo('家庭',$r('app.media.img12')),
      new classInfo('社交',$r('app.media.img13')),
      new classInfo('医疗',$r('app.media.img14')),
      new classInfo('学习',$r('app.media.img15')),
      new classInfo('宠物',$r('app.media.img16')),
      new classInfo('礼品',$r('app.media.img17')),
      new classInfo('办公',$r('app.media.img18')),
      new classInfo('维修',$r('app.media.img19')),
      new classInfo('捐赠',$r('app.media.img20')),
      new classInfo('红包',$r('app.media.img21')),
      new classInfo('还款',$r('app.media.img22')),
      new classInfo('借出',$r('app.media.img23')),
      new classInfo('其它',$r('app.media.img24'))
  ]
  //收入选项
  income: classInfo[] = [
      new classInfo('工资',$r('app.media.img25')),
      new classInfo('红包',$r('app.media.img21')),
      new classInfo('礼金',$r('app.media.img26')),
      new classInfo('分红',$r('app.media.img27')),
      new classInfo('理财',$r('app.media.img28')),
      new classInfo('借入',$r('app.media.img29')),
      new classInfo('收款',$r('app.media.img30')),
      new classInfo('其它',$r('app.media.img24'))
  ]

  //弹出框操作
  dialogController: CustomDialogController = new CustomDialogController({
    builder: amountPanel({onTaskConfirm: this.handleAddTask.bind(this)})
  })

  //对弹出框输入内容和类别进行操作
  handleAddTask(amount: number){
    if(this.showPayoutList){
      this.payout[this.index].money -= amount
      console.log('1')
    } else {
      this.income[this.index].money += amount
      console.log('2')
    }
    // this.dialogController.close()
  }

  build() {
    Column() {
      Row(){
        //标题栏:由返回按钮以及支出/收入操作组成
        Column(){
          Image($r('app.media.back'))
            .width(50)
            .borderRadius(50)
        }
        .width(50)
        .margin({right: 70})
        //用于显示支出列表的文字提示
        Text('支出')
          .fontSize(30)
          .fontColor(this.payoutColor)
          //点击事件,点击修改变量值
          .onClick(() => {
            this.payoutColor = '#ffff0000'
            this.incomeColor = '#ff000000'
            this.showPayoutList = true
          })
        Text(' | ')
          .fontSize(30)
        //用与显示收入列表的文本提示
        Text('收入')
          .fontSize(30)
          .fontColor(this.incomeColor)
          //点击事件,修改样式
          .onClick(() => {
            this.incomeColor = '#ffff0000'
            this.payoutColor = '#ff000000'
            this.showPayoutList = false
          })
      }
      .width('100%')
      .margin({bottom: 10})
      //账目类别列表
      Row(){
        //点击了支出文本,当前显示支出列表
        if(this.showPayoutList){
          //网格布局
          Grid(){
            ForEach(
              this.payout,
              (item: classInfo,index) => {
                GridItem(){
                  Column(){
                    //每个类别设计为单选按钮,点击会弹出弹出框输入金额
                    Radio({value: item.name, group: 'radioGroup'})
                      .checked(false)
                      .width(70)
                      .backgroundImage(item.img)
                      .backgroundImageSize({width:70,height:70})
                      .borderRadius(40)
                        //弹出弹出框,获取当前选中的类别
                      .onClick(() => {
                        this.index = index
                        this.image = item.img
                        this.name = item.name
                        this.dialogController.open()
                      })
                    Text(item.name)
                      .fontSize(20)
                  }
                  .width(80)
                  .margin({left: 7, top: 10})
                }
              }
            )
          }
          //四列
          .columnsTemplate('1fr 1fr 1fr 1fr')
          //点击了收入文本,展示收入列表
        } else {
          //网格布局
          Grid(){
            ForEach(
              this.income,
              (item: classInfo,index) => {
                GridItem(){
                  Column(){
                    //每个类别设计为单选按钮,点击会弹出弹出框输入金额
                    Radio({value: item.name, group: 'radioGroup'})
                      .checked(false)
                      .width(70)
                      .backgroundImage(item.img)
                      .backgroundImageSize({width:70,height:70})
                      .borderRadius(40)
                        //弹出弹出框,获取当前选中的类别
                      .onClick(() => {
                        this.index = index
                        this.image = item.img
                        this.name = item.name
                        this.dialogController.open()
                      })
                    Text(item.name)
                      .fontSize(20)
                  }
                  .width(80)
                  .margin({left: 7, top: 10})
                }
              }
            )
          }
          //四列
          .columnsTemplate('1fr 1fr 1fr 1fr')
        }
      }
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ffbfe8fd')
  }
}

弹出框页面布局:

//自定义弹出框样式设计:用于获取交易金额
@CustomDialog
export default struct amountPanel{
  //金额
  @State amount: number = 0
  //管理对话框状态
  controller: CustomDialogController
  //回调函数,用于完成任务时传递金额参数
  onTaskConfirm: (amount: number) => void

  build(){
    Column(){
      Row(){
        //金额输入框
        TextInput({placeholder: '点击输入金额...'})
          .backgroundColor(Color.White)
          //添加事件,当输入内容变化时触发
          .onChange(value => {
            this.amount = parseInt(value) //更新金额值
          })
      }
      .width('90%')
      .margin(25)
      Row(){
        Button('完成')
          .backgroundColor('#ff1280f6')
          //点击事件,调用确认任务的回调函数,传递当前amount值
          .onClick(() => {
            this.onTaskConfirm(this.amount)
          })
        Button('取消')
          .backgroundColor('#ff1280f6')
          //点击事件,关闭对话框
          .onClick(() => {
            this.controller.close()
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)
    }
    .width('100%')
    .height(150)
    .backgroundColor('#ff67d0f8')
    .borderRadius(30)
    .shadow({radius: 20, color: Color.Black,offsetX: 0,offsetY: 0})
  }
}

 存储图标信息的类(classInfo)包含两个变量:名称(name)、类别图片(img)和各类金额(money)。

export default class classInfo{
  //类别名称
  name: string
  //类别图片
  img: ResourceStr
  //每月类别花费金额
  money: number

  constructor(name: string, img: ResourceStr) {
    this.name = name
    this.img = img
    this.money = 0
  }

}

页面布局效果如下:

收支操作

四、账单页面布局

还未实现。

五、统计页面布局

该页面用于展示该月到目前为止,各类别的收支情况,通过List遍历。具体代码如下:

import classInfo from '../viewmodel/classInfo'
import budgetPanel from '../views/budgetPanel'

@Entry
@Component
struct statistics {
  //类别总计金额
  total: classInfo[] = [
    new classInfo('餐饮',$r('app.media.img1')),
    new classInfo('购物',$r('app.media.img2')),
    new classInfo('日用',$r('app.media.img3')),
    new classInfo('交通',$r('app.media.img4')),
    new classInfo('零食',$r('app.media.img5')),
    new classInfo('运动',$r('app.media.img6')),
    new classInfo('娱乐',$r('app.media.img7')),
    new classInfo('通讯',$r('app.media.img8')),
    new classInfo('服饰',$r('app.media.img9')),
    new classInfo('美容',$r('app.media.img10')),
    new classInfo('住房',$r('app.media.img11')),
    new classInfo('家庭',$r('app.media.img12')),
    new classInfo('社交',$r('app.media.img13')),
    new classInfo('医疗',$r('app.media.img14')),
    new classInfo('学习',$r('app.media.img15')),
    new classInfo('宠物',$r('app.media.img16')),
    new classInfo('礼品',$r('app.media.img17')),
    new classInfo('办公',$r('app.media.img18')),
    new classInfo('维修',$r('app.media.img19')),
    new classInfo('捐赠',$r('app.media.img20')),
    new classInfo('红包',$r('app.media.img21')),
    new classInfo('还款',$r('app.media.img22')),
    new classInfo('借出',$r('app.media.img23')),
    new classInfo('其它',$r('app.media.img24')),
    new classInfo('工资',$r('app.media.img25')),
    new classInfo('礼金',$r('app.media.img26')),
    new classInfo('分红',$r('app.media.img27')),
    new classInfo('理财',$r('app.media.img28')),
    new classInfo('借入',$r('app.media.img29')),
    new classInfo('收款',$r('app.media.img30'))
  ]

  build() {
    Column(){
      //标题栏,包括返回按钮和标题
      Row(){
        Image($r('app.media.back'))
          .width(50)
          .borderRadius(50)
        Text('类别收支统计')
          .fontSize(30)
          .margin({left: 10})
      }
      .width('100%')
      .margin({bottom: 40})
      //显示类别信息列表,用于显示每个类别月支出或收入金额
      Row(){
        List(){
          ForEach(
            this.total,
            (item: classInfo) => {
              ListItem(){
                Row(){
                  Row(){
                    Image(item.img)
                      .width(50)
                      .borderRadius(20)
                    Text(item.name)
                      .fontSize(20)
                      .margin({left: 5})
                  }
                  .margin({left: 10})
                  //支出类别金额为负
                  if(item.money < 0){
                    Text('-¥' + item.money.toFixed(2))
                      .fontSize(20)
                      .fontColor(Color.Red)
                    //收入类别金额为正
                  } else {
                    Text('¥' + item.money.toFixed(2))
                      .fontSize(20)
                      .fontColor(Color.Red)
                  }
                }
                .width('90%')
                .justifyContent(FlexAlign.SpaceBetween)
                .margin({top: 15})
              }
            }
          )
        }
      }
      .width('90%')
      .height(650)
      .backgroundColor(Color.White)
      .borderRadius(10)
      .shadow({radius: 20, color: Color.Black, offsetX: 0, offsetY: 0})
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ffbfe8fd')
  }
}

 布局样式效果如下:

六、我的页面布局

用于录入本月的预算金额,以及本月至目前为止支出总金额,收入总金额,剩余金额。具体代码如下:

import budgetPanel from '../views/budgetPanel'

@Entry
@Component
struct Mine {
  //预算
  @State budget: number = 0
  //总支出
  @State month_payout: number = 0
  //总收入
  @State month_income: number = 0
  //余额
  @State balance: number = 0

  //预算弹出框
  dialogController: CustomDialogController = new CustomDialogController({
    builder: budgetPanel({onTaskConfirm: this.handleAddTask.bind(this)})
  })

  //更新预算值
  handleAddTask(budget: number){
    this.budget = budget
    this.dialogController.close()
  }

  build() {
    Column(){
      //标题栏,包括返回按钮和标题
      Row(){
        Image($r('app.media.back'))
          .width(50)
          .borderRadius(50)
        Text('信息管理')
          .fontSize(30)
          .margin({left: 10})
      }
      .width('100%')
      .margin({bottom: 30})
        //预算显示设计
      Row() {
        Text('月预算:')
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
        Text('¥' + this.budget.toFixed(2))
          .fontSize(30)
          .fontColor(Color.Red)
        Image($r('app.media.budget'))
          .width(30)
          .margin({ left: 5 })
            //点击弹出弹出框,输入预算进行后续操作
          .onClick(() => {
            this.dialogController.open()
          })
      }
      .width('90%')
      .height(100)
      .backgroundColor(Color.White)
      .borderRadius(10)
      .border({width: {top: 2, bottom: 2, left: 2, right: 2} })
      .margin({bottom: 20})
      //本月收支情况的概述
      Row(){
        Column(){
          //剩余金额
          Row(){
            Text('本月结余')
              .fontSize(20)
              .fontWeight(FontWeight.Bold)
          }
          .width('100%')
          .padding({left: 10, right: 10})
          .margin({bottom: 15})
          Row(){
            if(this.balance < 0){
              Text('-¥' + this.month_payout.toFixed(2))
                .fontSize(30)
                .fontColor(Color.Red)
            } else {
              Text('¥' + this.month_payout.toFixed(2))
                .fontSize(30)
                .fontColor(Color.Red)
            }
          }
          .width('100%')
          .padding({left: 20})
          .margin({bottom: 15})
          //总支出
          Row(){
            Text('本月支出:')
              .fontSize(20)
            Text('-¥' + this.month_payout.toFixed(2))
              .fontSize(20)
              .fontColor(Color.Red)
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceBetween)
          .padding({left: 10, right: 10})
          .margin({bottom: 20})
          //总收入
          Row(){
            Text('本月收入:')
              .fontSize(20)
            Text('¥' + this.month_payout.toFixed(2))
              .fontSize(20)
              .fontColor(Color.Red)
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceBetween)
          .padding({left: 10, right: 10})
        }
      }
      .width('90%')
      .height(200)
      .backgroundColor(Color.White)
      .borderRadius(10)
      .border({width: {top: 2, bottom: 2, left: 2, right: 2} })
    }
    .width('100%')
    .height('100%')
    .backgroundImage($r("app.media.mine_background"))
    .backgroundImageSize({width: '100%', height: '100%'})
  }
}

布局样式效果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值