Harmony鸿蒙实战开发-记账app「可对时间进行分类管理,支出分析和收入分析-升级版」【源码在文末】

Harmony鸿蒙实战开发-记账app「可对时间进行分类管理,支出分析和收入分析-升级版」【源码在文末】


运行工具:DevEco Studio

一、运行演示

1、首页

image-20240721001243885

2、增加支出

image-20240721001306069 image-20240721001325710

3、支出分析

image-20240721001418036

4、增加收入

image-20240721001502470

5、收入分析

image-20240721001540763

6、增加后主页

二、部分代码

import relationalStore from '@ohos.data.relationalStore'
import router from '@ohos.router'
import CommonConstants from '../constants/CommonConstants'
import dbCreater from '../dbManage/dbCreater'
import dbManager from '../dbManage/dbManager'
import { ImageList } from '../model/AccountList'
import Record from '../model/record'
import showService from '../services/showServices'
import manageService from '../services/manageService';
import { DialogComponent } from '../view/DialogComponent'
import { DataAnlyseOutCome } from '../view/DataAnlyseOutCome'
import DateToStr from '../utils/DateToStr'
import { DataAnlyseInCome } from '../view/DataAnlyseInCome'
@Entry
@Component
struct Index {
  context = getContext()
  public store: relationalStore.RdbStore  // 用于初始化数据库
  @State showService: showService = null         // 用于展示数据的服务接口
  public manage_service: manageService = null       // 用于处理数据的服务接口
  @State list_records: Record[] = []                // 查询到的所有记录
  @State isSelected: boolean = false;
  tSearchController: SearchController = new SearchController()
  @State isSearch: boolean = false;
  @State isDateRange: boolean = false;
  @State searchIsDate: boolean = false;     // 搜索框是否是搜索分类
  @State searchText: string = '';
  seStartDate: Date = new Date("2010-1-1");
  seEndDate: Date = new Date("2010-1-1");
  @State showStartDate: string = '请选择开始时间';
  @State showEndDate: string = '请选择结束时间';
  @State selected_records: Record[] = []            // 选中分类的记录列表,只在isSelected为True时有用
  // 用于传递参数,包括详情弹窗 和 添加
  @State newAccount: Record = { id: 0, accountType: 0, typeText: '', amount: 0 , notes: '00', purpose: '00', date: '00'};
  // 用于分时间展示数据
  @State ca_list_records: any[] = []

  dialogController: CustomDialogController = new CustomDialogController( {
    builder: DialogComponent({
      newAccount: $newAccount,
      confirm: (newAccount: Record) => this.accept(newAccount)
    }),
    customStyle: true,
    alignment: DialogAlignment.Bottom
  });
  accept(b){

  }

  dcOutCome: CustomDialogController = new CustomDialogController({
    builder: DataAnlyseOutCome({
      newAccount: $list_records,
      shm : $showService,
      confirm: () => this.accept(this.isSelected)
    }),
    alignment: DialogAlignment.Bottom
  })

  dcInCome: CustomDialogController = new CustomDialogController({
    builder: DataAnlyseInCome({
      newAccount: $list_records,
      shm : $showService,
      confirm: () => this.accept(this.isSelected)
    }),
    alignment: DialogAlignment.Bottom
  })

  @Builder itemEnd(index: number) {
    // 侧滑后尾端出现的组件
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.app_delete'))
        .width(30)
        .height(30)
        .backgroundColor(Color.White)
    }
    .onClick(async () => {
      let de = await this.manage_service.deleteRecord(index)
      this.list_records = await this.showService.showAllRecord()
      this.mk_date_list_records_init()
    })

  }

  @Builder itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }

  async aboutToAppear() {
    // 初始化数据库
    let dbc = new dbCreater()
    await dbc.initialize(this.context)
    // 传递数据库操作对象
    this.store = dbc.store
    // 初始化数据库展示对象
    this.showService = new showService(this.store)
    // 初始化数据库操作对象
    let mm = new manageService()
    await mm.init()
    this.manage_service = mm

    // 获得所有记录
    this.list_records = await this.showService.showAllRecord()
    // 记录按时间分类
    this.mk_date_list_records_init()

  }
  async onPageShow(){
    // 每次页面切换的时候,都要重新刷新列表
    // 从添加页面转回来,需要重新刷新,显示新增数据,也需要重新分类
    this.list_records = await this.showService.showAllRecord()
    this.mk_date_list_records_init()
    console.info('onPageShow');
  }

  build() {

    Column() {
      Column() {
        Row(){
          Select([{ value: '支出'},
            { value: '收入' },
            {value: '所有'}
          ])
            .selected(2)
            .value('所有')
            .font({ size: 16, weight: 500 })
            .fontColor('#182431')
            .selectedOptionFont({ size: 16, weight: 400 })
            .optionFont({ size: 16, weight: 400 })
            .onSelect((index: number) => {
              console.info('Select:' + index)
              if(index === 2){
                this.isSelected = false;
                this.mk_date_list_records_init()
              }
              else {
                this.selected_show(index)
                this.mk_date_list_records_init()
              }
            })
          Button('支出分析')
            .onClick(()=>{
              this.dcOutCome.open()
            })
          Button('收入分析')
            .onClick(()=>{
              this.dcInCome.open()
            })
        }
        .width('100%')
        .justifyContent(FlexAlign.Start)

        Row(){
          Select([{ value: '时间'},
            { value: '分类' }
          ])
            .selected(1)
            .value('分类')
            .font({ size: 16, weight: 500 })
            .fontColor('#182431')
            .selectedOptionFont({ size: 16, weight: 400 })
            .optionFont({ size: 16, weight: 400 })
            .onSelect((index: number) => {
              console.info('Select:' + index)
              if(index === 0){
                this.searchIsDate = true;
              }
              else {
                this.searchIsDate = false;
                this.isDateRange = false;
                this.mk_date_list_records_init();
              }
            })
          if (this.searchIsDate){
            Row({space: 5}){
              Text('从')
              Text(this.showStartDate)
                .onClick(()=>{
                  DatePickerDialog.show({
                    start: new Date("2000-1-1"),
                    end: new Date("2100-12-31"),
                    selected: this.seStartDate,
                    onAccept: (value: DatePickerResult) => {
                      this.seStartDate.setFullYear(value.year, value.month, value.day)
                      this.showStartDate = DateToStr(this.seStartDate)
                    },
                    onCancel: () => {},
                    onChange: (value: DatePickerResult) => {
                      this.seStartDate.setFullYear(value.year, value.month, value.day)
                      this.showStartDate = DateToStr(this.seStartDate)
                    }
                  })
                })
                .fontColor('#ff507ae9')
              Text('到')
              Text(this.showEndDate)
                .onClick(()=>{
                  DatePickerDialog.show({
                    start: new Date("2000-1-1"),
                    end: new Date("2100-12-31"),
                    selected: this.seEndDate,
                    onAccept: (value: DatePickerResult) => {
                      this.seEndDate.setFullYear(value.year, value.month, value.day)
                      this.showEndDate = DateToStr(this.seEndDate)
                    },
                    onCancel: () => {},
                    onChange: async (value: DatePickerResult) => {
                      this.seEndDate.setFullYear(value.year, value.month, value.day)
                      this.showEndDate = DateToStr(this.seEndDate)
                      if (this.entureDateRight(this.seStartDate, this.seEndDate)) {

                      }
                      this.selected_records = await this.showService.getRecordsByTimeRange(this.showStartDate, this.showEndDate);
                      this.isDateRange = true;
                      this.mk_date_list_records_init()
                    }
                  })
                })
                .fontColor('#ff507ae9')
            }
            .width(300)
            .height(60)
            .justifyContent(FlexAlign.Start)

          }
          else{
            Search({ value: this.searchText, placeholder: 'Type to search...', controller: this.tSearchController })
              .searchButton('SEARCH')
              .backgroundColor('#F5F5F5')
              .placeholderColor(Color.Grey)
              .placeholderFont({ size: 14, weight: 400 })
              .textFont({ size: 14, weight: 400 })
              .onSubmit((value: string) => {
                this.searchText = value
                if (this.searchText === '') {
                  this.isSearch = false
                  this.mk_date_list_records_init()
                }
                else{
                  this.isSearch = true
                  this.searchShow()
                  this.mk_date_list_records_init()
                }
              })
              .margin(10)
              .width(280)
          }

        }
      }
      .width('100%')
      .height(105)
      .backgroundColor('#ffb3f1ec')

      List() {
        ForEach(this.ca_list_records, li =>{
          ListItemGroup({ header: this.itemHead(li.date) }){
            ForEach(li.list_records, (item: Record) => {
              ListItem() {
                Row() {
                  Image(ImageList[item.typeText])
                    .width('40vp')
                    .aspectRatio(CommonConstants.FULL_SIZE)
                    .margin({ right: '16vp' })

                  Text(item.typeText)
                    .height('22vp')
                    .fontSize('16vp')

                  Blank()
                    .layoutWeight(1)
                  Text(item.accountType === 0 ? '-' + item.amount.toString() : '+' + item.amount.toString())
                    .fontSize('16vp')
                    .fontColor(item.accountType === 0 ? '#FFE84826' : '#FF007DFF')
                    .align(Alignment.End)
                    .flexGrow(CommonConstants.FULL_SIZE)
                }

              }
              .swipeAction({ end: this.itemEnd.bind(this, item.id) }) // 设置侧滑属性
              .height(100)
              .onClick(()=>{
                this.newAccount = item;
                this.dialogController.open()
              })

            }, item => item.id)
          }
        })

      }
      .alignListItem(ListItemAlign.Start)
      .width('100%')
      Button(){
        Image($rawfile('add.png'))
      }
      .width('48vp')
      .height('48vp')
      .position({ x: CommonConstants.EDIT_POSITION_X, y: CommonConstants.EDIT_POSITION_Y })
      .onClick(() => {
        this.newAccount = { id: 0, accountType: 0, typeText: '', amount: 0 , notes: '', purpose: '', date: ''};
        onJumpClick()
      })
    }
    .width('100%')
    .height('100%')
    // .justifyContent(FlexAlign.SpaceBetween)

  }

  mk_date_list_records_init(){
    // 根据不同的时间,分类记录
    // 分类列表的元素是json数据
    // 其中第一项date是时间
    // 第二项list_records 是一个数组,存的是同一时间的记录
    const date_set = new Set<String>()
    this.ca_list_records = []
    // 根据分类选择
    const nn = (this.isSelected || this.isSearch || this.isDateRange) ? this.selected_records: this.list_records;

    for (let i = 0; i < nn.length; i++){
      const record = nn[i];
      if(date_set.has(record.date)){

        for (let index = 0; index < this.ca_list_records.length; index++) {
          const element = this.ca_list_records[index];
          if (element.date === record.date) {
            this.ca_list_records[index].list_records.push(record)
            break;
          }
        }

      }

      else{
        date_set.add(nn[i].date)
        this.ca_list_records.push({
          date: nn[i].date,
          list_records: [nn[i]]
        })
      }
    }
    console.info('这是' + this.ca_list_records)
  }

  selected_show(ii: number){
    // const mm = (this.isSelected || this.isDateRange) ? this.selected_records : this.list_records
    const mm = this.list_records;
    this.selected_records = []
    for (let index = 0; index < mm.length; index++) {
      const element = mm[index];
      if(element.accountType == ii){
        this.selected_records.push(element)
      }
    }
    this.isSelected = true
  }
  searchShow(){
    if (!this.isSearch) {
      return;
    }
    const mm = (this.isSelected || this.isDateRange) ? this.selected_records : this.list_records
    this.selected_records = []
    for (let index = 0; index < mm.length; index++) {
      const element = mm[index];
      if (element.typeText === this.searchText) {
        this.selected_records.push(element)
      }
    }
    this.isSearch = true
  }

  entureDateRight(st: Date, end: Date){
    if(st > end){
      return false
    }
  }

}

// 在Home页面中
function onJumpClick(): void {
  const valueBucket1: Record =  {
    id: -1,
    amount: 50.0,
    notes: '00',
    typeText: '00',
    date: '00',
    accountType: 0,
    purpose: '00'
  };
  router.pushUrl({
    url: 'pages/add', // 目标url
    params: valueBucket1
  }, router.RouterMode.Standard, (err) => {
    if (err) {
      console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
      return;
    }
    console.info('Invoke pushUrl succeeded.');
  });
}

三、源码

相关鸿蒙项目点此专栏

image-20241020235757199

通过百度网盘分享的文件:…zip 链接:百度网盘 请输入提取码

文件已经加密,请点击下方名片获取源码

或:Lvnvn0508

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值