diff --git a/src/pages/Home/Home.module.scss b/src/pages/Home/Home.module.scss index 53573cd..23ca8c7 100644 --- a/src/pages/Home/Home.module.scss +++ b/src/pages/Home/Home.module.scss @@ -1,22 +1,21 @@ -.home { - height: 100%; - display: flex; - flex-direction: column; - .total { - display: flex; - flex-direction: row; - justify-content: flex-start; - gap: 10px; - align-items: flex-start; - } - - .monthBar { - height: 300px; - } - - .cards { - display: flex; - flex-direction: row; - justify-content: right; - } -} +.home { + height: 100%; + display: flex; + flex-direction: column; + .total { + display: flex; + flex-direction: row; + justify-content: flex-start; + gap: 10px; + align-items: flex-start; + } + + .monthBar { + height: 300px; + } + + .cards { + display: grid; + grid: repeat(2, auto) / auto-flow auto; + } +} diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 0695ad2..ee3ff60 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,123 +1,137 @@ -import styles from "./Home.module.scss" -import * as R from 'ramda' -import { BillType, IBill } from "../../model" -import Bar from "../../components/charts/bar" -import { useContext, useEffect, useState } from "react"; -import { BillContext } from "../../store"; -import { observer } from "mobx-react-lite"; -import Pie from "../../components/charts/pie"; -import { - Card, - Modal, - DatePicker, - Radio, - Space, -} from "antd"; -import moment from 'moment'; -import 'moment/locale/zh-cn'; -import dayjs from 'dayjs' - -const Home = () => { - const billStore = useContext(BillContext) - - const transformer = (record: Record) => { - const funcs = R.compose( - R.sort((a: { x: string, y: number }, b) => { - const date1 = dayjs(a.x).toDate().getTime() - const date2 = dayjs(b.x).toDate().getTime() - - return date1 - date2 - }), - R.map((key: string) => { - const moneys = record[key].map(bill => bill.money) - return { - x: key, - y: Number(R.sum(moneys).toFixed(2)), - } - })) - return funcs(R.keys(record)) - } - - const now = new Date(); - const [year, setYear] = useState(now.getFullYear()) - const [month, setMonth] = useState(now.getMonth() + 1) - - useEffect(() => { - billStore.fetch(year, month).then() - }, [year, month]) - - const changeDate = (date: moment.Moment | null, datestring: string) => { - const d = date?.toDate() ?? new Date() - setYear(d.getFullYear()) - setMonth(d.getMonth() + 1) - } - - const typeOpt = [ - { label: '支出', value: BillType.consume }, - { label: '收入', value: BillType.income }, - ]; - const [billType, setBillType] = useState(BillType.consume) - - // 点击bar弹出当天pie - const [isModalOpen, setIsModalOpen] = useState(false); - const [modalTitle, setModalTitle] = useState(""); - const [modalData, setModalData] = useState<{ - x: string - y: number - }[]>([]); - - return ( -
-
- - - setBillType(e.target.value)} - /> - - {"总金额"} - ¥{billStore.getTotalMoney(billType)} - - -
-
- { - setIsModalOpen(true) - setModalTitle(date) - setModalData(transformer(billStore.groupByClass(billType, date))) - }} - /> -
-
- - - -
- setIsModalOpen(false)} - onCancel={() => setIsModalOpen(false)} - title={modalTitle} - > - - -
- ) -} - +import styles from "./Home.module.scss" +import * as R from 'ramda' +import { BillType, IBill } from "../../model" +import Bar from "../../components/charts/bar" +import { useContext, useEffect, useState } from "react"; +import { BillContext } from "../../store"; +import { observer } from "mobx-react-lite"; +import Pie from "../../components/charts/pie"; +import { + Card, + Modal, + DatePicker, + Radio, + Space, +} from "antd"; +import moment from 'moment'; +import 'moment/locale/zh-cn'; +import dayjs from 'dayjs' + +const Home = () => { + const billStore = useContext(BillContext) + + const transformer = (record: Record) => { + const funcs = R.compose( + R.sort((a: { x: string, y: number }, b) => { + const date1 = dayjs(a.x).toDate().getTime() + const date2 = dayjs(b.x).toDate().getTime() + + return date1 - date2 + }), + R.map((key: string) => { + const moneys = record[key].map(bill => bill.money) + return { + x: key, + y: Number(R.sum(moneys).toFixed(2)), + } + })) + return funcs(R.keys(record)) + } + + const now = new Date(); + const [year, setYear] = useState(now.getFullYear()) + const [month, setMonth] = useState(now.getMonth() + 1) + + useEffect(() => { + billStore.fetch(year, month).then() + }, [year, month]) + + const changeDate = (date: moment.Moment | null, datestring: string) => { + const d = date?.toDate() ?? new Date() + setYear(d.getFullYear()) + setMonth(d.getMonth() + 1) + } + + const typeOpt = [ + { label: '支出', value: BillType.consume }, + { label: '收入', value: BillType.income }, + ]; + const [billType, setBillType] = useState(BillType.consume) + + // 点击bar弹出当天pie + const [isModalOpen, setIsModalOpen] = useState(false); + const [modalTitle, setModalTitle] = useState(""); + const [modalData, setModalData] = useState<{ + x: string + y: number + }[]>([]); + + // 显示单个cls的饼状图,查看cls内部的label的消费情况, + // 这里有一个cls列表 + const clsesForShow = ["餐饮", "恋爱"] + + return ( +
+
+ + + setBillType(e.target.value)} + /> + + {"总金额"} + ¥{billStore.getTotalMoney(billType)} + + +
+
+ { + setIsModalOpen(true) + setModalTitle(date) + setModalData(transformer(billStore.groupByClass(billType, date))) + }} + /> +
+
+ + + + { + clsesForShow.map(cls => { + return ( + + ) + }) + } +
+ setIsModalOpen(false)} + onCancel={() => setIsModalOpen(false)} + title={modalTitle} + > + + +
+ ) +} + export default observer(Home) \ No newline at end of file diff --git a/src/store/index.ts b/src/store/index.ts index 5ce0866..a2fcae3 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,123 +1,133 @@ -import { makeAutoObservable, runInAction } from "mobx"; -import { createContext } from "react"; -import { createBill, getBills, getClass } from "../api/bills"; -import { BillType, IBill } from "../model"; -import * as R from "ramda" - -/** - * 仅存储一个月的数据 - */ -export class Bill { - private _bills: IBill[] = []; - // _cls2label: IClass = {consume: new Map(), income: []} - private _cls2label: { consume: Record, income: [] } = { consume: {}, income: [] } - - constructor() { - makeAutoObservable(this) - this.fetchClass().then() - } - - get bills() { - return this._bills - } - - get cls2label() { - return this._cls2label - } - - groupByDate(type?: BillType) { - const classFun = R.filter((bill: IBill) => R.of(bill.type).length === 0 || bill.type === type) - const functions = R.compose( - R.groupBy((bill: IBill) => bill.date), - classFun, - ) - return functions(this._bills) - } - - groupByClass(type?: BillType, date?: string) { - const classFun = R.filter((bill: IBill) => R.of(bill.type).length === 0 || bill.type === type && (date ? bill.date === date : true)) - const functions = R.compose( - R.groupBy((bill: IBill) => bill.cls), - classFun, - ) - return functions(this._bills) - } - - get listDailyMoney() { - return this.groupByDate - } - - get totalMoney() { - const functions = R.compose( - Number, - (s: number) => s.toFixed(2), - R.sum, - R.map((bill: IBill) => bill.money) - ) - return functions(this._bills) - } - - get consumeMoney() { - const functions = R.compose( - Number, - (s: number) => s.toFixed(2), - R.sum, - R.map((bill: IBill) => bill.money), - R.filter((bill: IBill) => bill.type === BillType.consume), - ) - return functions(this._bills) - } - - get incomeMoney() { - const functions = R.compose( - Number, - (s: number) => s.toFixed(2), - R.sum, - R.map((bill: IBill) => bill.money), - R.filter((bill: IBill) => bill.type === BillType.income), - ) - return functions(this._bills) - } - - getTotalMoney(type?: BillType) { - switch (type) { - case BillType.income: - return this.incomeMoney - case BillType.consume: - return this.consumeMoney - default: - return this.totalMoney - } - } - - get meanMoneyByDate() { - const days = Reflect.ownKeys(this.groupByDate).length - if (days === 0) return 0 - return this.totalMoney / days - } - - - async add(bill: IBill) { - const { id } = await createBill(bill) - bill.id = id - runInAction(() => { - this._bills.push(bill); - }) - } - - async fetch(year: number, month: number) { - const data = await getBills(year, month) - runInAction(() => { - this._bills = data - }) - } - - async fetchClass() { - const cls2label = await getClass() - runInAction(() => { - this._cls2label = cls2label - }) - } -} - -export const BillContext = createContext(new Bill()); +import { makeAutoObservable, runInAction } from "mobx"; +import { createContext } from "react"; +import { createBill, getBills, getClass } from "../api/bills"; +import { BillType, IBill } from "../model"; +import * as R from "ramda" + +/** + * 仅存储一个月的数据 + */ +export class Bill { + private _bills: IBill[] = []; + // _cls2label: IClass = {consume: new Map(), income: []} + private _cls2label: { consume: Record, income: [] } = { consume: {}, income: [] } + + constructor() { + makeAutoObservable(this) + this.fetchClass().then() + } + + get bills() { + return this._bills + } + + get cls2label() { + return this._cls2label + } + + groupByDate(type?: BillType) { + const classFun = R.filter((bill: IBill) => R.of(bill.type).length === 0 || bill.type === type) + const functions = R.compose( + R.groupBy((bill: IBill) => bill.date), + classFun, + ) + return functions(this._bills) + } + + groupByClass(type?: BillType, date?: string) { + const classFun = R.filter((bill: IBill) => R.of(bill.type).length === 0 || bill.type === type && (date ? bill.date === date : true)) + const functions = R.compose( + R.groupBy((bill: IBill) => bill.cls), + classFun, + ) + return functions(this._bills) + } + + groupByLabelOfClass(className: string) { + const classFun = R.filter((bill: IBill) => R.of(bill.type).length === 0 || bill.cls === className) + const functions = R.compose( + R.groupBy((bill: IBill) => bill.label), + classFun, + ) + return functions(this._bills) + } + + + get listDailyMoney() { + return this.groupByDate + } + + get totalMoney() { + const functions = R.compose( + Number, + (s: number) => s.toFixed(2), + R.sum, + R.map((bill: IBill) => bill.money) + ) + return functions(this._bills) + } + + get consumeMoney() { + const functions = R.compose( + Number, + (s: number) => s.toFixed(2), + R.sum, + R.map((bill: IBill) => bill.money), + R.filter((bill: IBill) => bill.type === BillType.consume), + ) + return functions(this._bills) + } + + get incomeMoney() { + const functions = R.compose( + Number, + (s: number) => s.toFixed(2), + R.sum, + R.map((bill: IBill) => bill.money), + R.filter((bill: IBill) => bill.type === BillType.income), + ) + return functions(this._bills) + } + + getTotalMoney(type?: BillType) { + switch (type) { + case BillType.income: + return this.incomeMoney + case BillType.consume: + return this.consumeMoney + default: + return this.totalMoney + } + } + + get meanMoneyByDate() { + const days = Reflect.ownKeys(this.groupByDate).length + if (days === 0) return 0 + return this.totalMoney / days + } + + + async add(bill: IBill) { + const { id } = await createBill(bill) + bill.id = id + runInAction(() => { + this._bills.push(bill); + }) + } + + async fetch(year: number, month: number) { + const data = await getBills(year, month) + runInAction(() => { + this._bills = data + }) + } + + async fetchClass() { + const cls2label = await getClass() + runInAction(() => { + this._cls2label = cls2label + }) + } +} + +export const BillContext = createContext(new Bill());