页面布局实现
一、首页布局
首页具体功能为:提供一个导航栏,分别为明细、账单、添加账目信息、收支统计、我的信息五个图标,分别用于跳转到各自页面进行操作。具体代码如下:
@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%'})
}
}
布局样式效果如下: