用Vue和firebase开发一个私人记账APP

2023-07-24 中文

技术

Vue.js Vite.js Firebase

前后端分离开发一个记账APP,前端用Vite和Vue,后端用firebase,开发一周就上线了,真的很vite(快)。

前言

用 Notion 记了半年的账,现在觉得有以下缺点:新建账单步骤太多,应用账单模板不够方便,账单列表瀑布流加载非常不顺畅,搜索关键字速度很慢,按月汇总计算需要每个月手动激活,查看汇总需要切换好几个页面。

总之就是越来越不好用了,不如我自己写一个。

写好了,项目地址: https://github.com/c53hzn/april-bill

参考资料

從零開始的個人化記帳程式開發

文章介绍了一个记账APP的开发过程,前端用Vue,后端用Firebase。作者使用了 Vue CLI 作为构建工具,但是我决定使用 Vite ,因为之前的使用体验告诉我, Vite 是比 Vue CLINuxt 更快的构建工具。

让用户使用电子邮件地址和密码登录

这是关于Firebase用户登录的文档,文章介绍了firebase的用户注册、登入、登出功能。由于我要做一个私人APP,不需要陌生人来注册,那就只要登入登出即可。

大家可能会想,那如果要在刷新页面或切换页面后保持登录状态,是不是要用到 Vuex store 呢?但是我只有一个页面,所有功能都在同一版面,那也用不到 store 了。

功能设计

APP的入口页面应当是一个SPA的静态网页,可以使用 GitHub pages 部署,然后在登入后动态加载 Firebase 的数据。

需要实现的功能大概是这样:

用户验证

  • 登入/登出
  • 保持登入状态(用localStorage储存uid,不主动登出就永远保持登入)

加载账单

  • 显示默认时间区间的账单(默认时间过去一个月,会根据月份横跨不同的天数)
  • 设置每页显示条数
  • 筛选特定时间区间的账单(加载一次call一次API)
  • 显示交易汇总(加载一次计算一次)
  • 分页显示(翻页不call API)
  • 在本页账单内搜索关键字(搜索不call API)

查看汇总

  • 查看账户流水汇总
  • 查看分类交易汇总

账户和分类设置

  • 编辑账户名和类型(更新数据库,刷新选项)
  • 编辑分类名和类型(更新数据库,刷新选项)

账单操作

  • 新增空白账单(更新数据库,重新加载账单)
  • 根据收入或支出性质来加载分类和账户的选项
  • 查看现有账单
  • 更新现有账单(更新数据库,重新加载账单)
  • 另存现有账单(更新数据库,重新加载账单)
  • 删除现有账单(更新数据库,重新加载账单)
  • 新建/删除账单模板(更新数据库,重新加载模板)
  • 应用账单模板

系统界面UI设计

登录页面

image

登入后页面

image

编辑账户选项

image

编辑分类选项

image

新增/修改/查看账单

image

新增/修改/应用账单模板

image

查看账户交易汇总

image

查看分类交易汇总

image

数据库和服务器

本项目使用Firebase的Firestore数据库服务,目前用量为 spark 套餐,暂不收费。

数据结构

账户(ACCOUNT)

{
    id: "ACC001",
    TYPE: "信用卡",
    NAME: "信用卡A",
    initialBalance: 0,
    balance: 0
}

分类(CATEGORY)

{
    id: "CAT001",
    TYPE: "支出",
    NAME: "生活缴费"
}

账单(BILL)

{
    id: "BILL202307240101",
    DATE: "2023-07-24",
    NATURE: "支出",
    CATEGORY: "饮食",
    NAME: "晚饭",
    ACC_IN: "",
    ACC_OUT: "信用卡A",
    AMOUNT: 45.5,
    REMARK: "",
    NOTES: ""
}

账单模板(BILL_template)

{
    id: "template001",
    DATE: "",
    NATURE: "支出",
    CATEGORY: "饮食",
    NAME: "晚饭",
    ACC_IN: "",
    ACC_OUT: "信用卡A",
    AMOUNT: 45.5,
    REMARK: "",
    NOTES: ""
}

在firebase项目里新建一个用户,拿到 uid ,然后在firestore里建立一个账本集合 BOOK ,再用 uid 作为 BOOK 之下的文档的 id ,然后在 uid 这个文档之下新建上面四个集合,分别是 ACCOUNT CATEGORY BILL BILL_template

假设我们要调用 id 为 BILL202307240101 的账单文档,调用的路径为

var path = `BOOK/${uid}/BILL/BILL202307240101`;

然后在登录之后拿到这个 uid 就可以操作自己的私人账单了。

开发模式转生产模式

在Firebase上创建项目的时候,一开始系统会让你选是要开发模式还是生产模式,如果选了开发模式,那么接下来30天的时间里,任何拿到你的客户端credentials的人都可以操作你的数据库。

这是靠项目设置里面的“安全规则”来实现的,如果需要从开发模式转到生产模式,其实数据上不需要做任何操作,只需要把项目设置里的安全规则改一改就行了。

用户验证及权限设置

在firebase的 Authentication 之下新建一个邮箱密码的登录账号,拿到 uid

开发的时候设计一下用 uid 验证登录,抄下这个 uid ,一会我们用得到。

在firebase的 firestore database里面设置 rule

原本默认有一条规则,设置的时间是项目创建后一个月的日期,这条规则的意思是“允许创建后一个月之内的读写操作”,这就算是“开发模式”了,默认你只开发一个月吗?😱

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
    allow read, write: if request.time < timestamp.date(${year}, ${month}, ${day});
    }

  }
}

我们把它改成这样,意思是“允许uid是这个的用户进行读写”,取消了时间限制就,新增了一个用户限制,就保证了只有我自己输入的这个uid才能操作。

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth.uid == ${uid};
    }
  }
}

为什么要限制仅允许一个 uid ?

因为我是开发给我自己用的,我希望即使别人拿到我的firebase credentials也用不了它。

导出Notion账单并转移到APP

之前在 Notion 上记了半年的账,导出来csv一看,其实也才几百条,不知道为什么 Notion 加载得越来越慢,不过这次转移数据之后,这些都不重要了。

在Excel或者WPS里面打开csv文件,将表头改成 id DATE NAME NATURE CATEGORY ACC_OUT ACC_IN AMOUNT REMARK NOTES,然后用公式自动生成 BILL202307240101 格式的 id,保存csv。

此时要注意,我的APP需要的日期格式是 YYYY-MM-DD ,但是Excel或者WPS有个坏习惯,就是在识别到日期的时候自动转成 YYYY/M/D 的格式,所以我们需要手动调整一下,改对了之后再保存。

然后在网上搜一个 csv to json 的工具,上传 csv 文件,拿到我们需要的 json 数据,此时不要保存成 json。为了方便使用,我们把它存成这样的 bills.js 文件。

var bills = [{
    id: "BILL202307240101",
    DATE: "2023-07-24",
    NATURE: "支出",
    CATEGORY: "饮食",
    NAME: "晚饭",
    ACC_IN: "",
    ACC_OUT: "信用卡A",
    AMOUNT: 45.5,
    REMARK: "",
    NOTES: ""
}];

然后在同文件夹下新建一个 app.html 文件,内容如下

<head>
    <meta charset="UTF-8">
</head>

<body>
  asdf
</body> 

<script src="./bills.js"></script>

<script type="module">
  import { initializeApp } from "https://www.gstatic.com/firebasejs/10.0.0/firebase-app.js";
  import { getFirestore,doc,setDoc,collection  } from "https://www.gstatic.com/firebasejs/10.0.0/firebase-firestore.js";


  // Your web app's Firebase configuration
  const firebaseConfig = {
    apiKey: "***",
    authDomain: "***.firebaseapp.com",
    projectId: "***",
    storageBucket: "***.appspot.com",
    messagingSenderId: "***",
    appId: "***"
  };

  // Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);


for (let i = 0; i < bills.length; i++) {
    addBill(bills[i]);
}

async function addBill(bill) {
    await setDoc(doc(db, `BOOK/${uid}/BILL`, bill.id), bill);
}
</script>

这是一个极简的导入账单的网页,直接在本机使用即可。

网页一打开就会开始上传账单,网页和浏览器控制台不显示结果,但是你可以到浏览器的开发者工具的“网络”标签页查看,等到上传任务不再增多,就说明上传完了。

这里有一个很重要的地方,那就是 <head></head> 里面一定要写明此html文档的文字编码

<meta charset="UTF-8">

如果不写明是 UTF-8 ,那么上传内容有中文的时候会变成乱码,而写了 UTF-8 就能上传正常的内容了。

总结

以上就是本项目的要点,其他的都在代码里了。

如果有什么问题,欢迎大家和我交流。

全文完~

Load Comments