feat🔫: bill表格

This commit is contained in:
clz 2022-09-06 17:51:46 +08:00
parent 6b54a5dc9d
commit d7659d83b9
5 changed files with 117 additions and 16 deletions

View File

@ -17,6 +17,7 @@
.content { .content {
flex: 1 1; flex: 1 1;
padding: 10px; padding: 10px;
overflow: auto;
} }
.footer { .footer {
} }

View File

@ -1,10 +1,10 @@
export const useBill = () => { export const useBill = () => {
// const [bills, setBills] = useState<IBill[]>([]) // const [_bills, setBills] = useState<IBill[]>([])
// //
// useEffect(() => { // useEffect(() => {
// getBills().then(setBills).catch(console.dir) // getBills().then(setBills).catch(console.dir)
// }, []) // }, [])
// return { // return {
// bills, // _bills,
// } // }
} }

View File

@ -1,2 +1,14 @@
.record { .record {
height: 100%;
display: flex;
flex-direction: column;
justify-content: start;
}
.new {
}
.table {
flex: 1 1;
overflow: auto;
} }

View File

@ -6,15 +6,21 @@ import {
message, message,
Radio, Radio,
Select, Select,
Space Space, Table
} from "antd"; } from "antd";
import {
ArrowDownOutlined,
CloudUploadOutlined, DeleteOutlined,
} from "@ant-design/icons";
import {useContext, useEffect, useRef, useState} from "react"; import {useContext, useEffect, useRef, useState} from "react";
import {BillType, EmptyBill} from "../../model"; import {BillType, EmptyBill, IBill} from "../../model";
import moment from "moment/moment"; import moment from "moment/moment";
import {BillContext} from "../../store"; import {BillContext} from "../../store";
import {observer} from "mobx-react-lite"; import {observer} from "mobx-react-lite";
import styles from "./Record.module.scss" import styles from "./Record.module.scss"
import {BaseSelectRef} from "rc-select/lib/BaseSelect"; import {BaseSelectRef} from "rc-select/lib/BaseSelect";
import {createBill} from "../../api/bills";
function Record() { function Record() {
@ -35,12 +41,56 @@ function Record() {
}, [clsRef]) }, [clsRef])
// table
const columns = [
{
title: "日期",
dataIndex: "date",
key: "date",
},
{
title: "类别",
dataIndex: "cls",
key: "cls",
},
{
title: "标签",
dataIndex: "label",
key: "label",
},
{
title: "金额",
dataIndex: "money",
key: "money",
},
{
title: "备注",
dataIndex: "options",
key: "options",
},
{
title: "操作",
key: 'action',
render: (_: any, record: any) => (
<Space>
<Button
type="primary"
danger
icon={<DeleteOutlined/>}
onClick={() => setDataSource(datasource.filter(bill => bill !== record))}
/>
</Space>
),
},
]
const [datasource, setDataSource] = useState<IBill[]>([])
const typeOpt = [ const typeOpt = [
{label: '支出', value: BillType.consume}, {label: '支出', value: BillType.consume},
{label: '收入', value: BillType.income}, {label: '收入', value: BillType.income},
]; ];
// 提交到表格
const submit = async () => { const submit = async () => {
const bill = EmptyBill() const bill = EmptyBill()
bill.type = billType bill.type = billType
@ -63,12 +113,32 @@ function Record() {
} }
if (checkBill()) { if (checkBill()) {
await billStore.add(bill) Reflect.set(bill, "key", crypto.randomUUID())
setDataSource([bill, ...datasource])
reset() reset()
} else { } else {
message.error("请输入完整") message.error("请输入完整")
} }
} }
// 上传云端
const [uploadLoading, setUploadLoading] = useState(false)
const upload = async () => {
setUploadLoading(true)
const failures = []
for (let bill of datasource) {
try {
const {id} = await createBill(bill)
if (!id) failures.push(bill)
} catch (e) {
failures.push(bill)
}
}
setDataSource(failures)
setUploadLoading(false)
}
return ( return (
<div className={styles.record}> <div className={styles.record}>
<div className={styles.new}> <div className={styles.new}>
@ -81,6 +151,7 @@ function Record() {
onChange={e => setBillType(e.target.value)} onChange={e => setBillType(e.target.value)}
/> />
<DatePicker <DatePicker
allowClear={false}
value={moment(date, 'YYYY-MM-DD')} value={moment(date, 'YYYY-MM-DD')}
onChange={(_, dateStr) => setDate(dateStr)} onChange={(_, dateStr) => setDate(dateStr)}
/> />
@ -117,6 +188,7 @@ function Record() {
cls2label.consume[cls] cls2label.consume[cls]
.map(la => <Select.Option key={la} value={la}>{la}</Select.Option>) .map(la => <Select.Option key={la} value={la}>{la}</Select.Option>)
} }
<Select.Option key={"other"} value={"其他"}>{"其他"}</Select.Option>)
</Select> </Select>
<InputNumber <InputNumber
style={{width: 120}} style={{width: 120}}
@ -135,14 +207,27 @@ function Record() {
/> />
<Button <Button
type="primary" type="primary"
icon={<ArrowDownOutlined/>}
onKeyUp={e => e.key === "Tab" onKeyUp={e => e.key === "Tab"
&& clsRef.current!.focus() && clsRef.current!.focus()
} }
onClick={submit} onClick={submit}
></Button> >
</Button>
</Space> </Space>
</div> </div>
<div className={styles.table}></div> <div className={styles.table}>
<Table dataSource={datasource} columns={columns}></Table>
<Button
icon={<CloudUploadOutlined/>}
type="primary"
loading={uploadLoading}
onClick={upload}
>
</Button>
</div>
</div> </div>
) )
} }

View File

@ -8,25 +8,28 @@ import * as R from "ramda"
* *
*/ */
export class Bill { export class Bill {
bills: IBill[] = []; private _bills: IBill[] = [];
// _cls2label: IClass = {consume: new Map<string, string[]>(), income: []} // _cls2label: IClass = {consume: new Map<string, string[]>(), income: []}
_cls2label: { consume: Record<string, string[]>, income: [] } = {consume: {}, income: []} private _cls2label: { consume: Record<string, string[]>, income: [] } = {consume: {}, income: []}
constructor() { constructor() {
makeAutoObservable(this) makeAutoObservable(this)
this.fetchClass().then() this.fetchClass().then()
} }
get bills() {
return this._bills
}
get cls2label() { get cls2label() {
return this._cls2label return this._cls2label
} }
get listAllByDate() { get listAllByDate() {
return R.groupBy((bill: IBill) => bill.date)(this.bills) return R.groupBy((bill: IBill) => bill.date)(this._bills)
} }
get listAllByClass() { get listAllByClass() {
return R.groupBy((bill: IBill) => bill.cls)(this.bills) return R.groupBy((bill: IBill) => bill.cls)(this._bills)
} }
get listDailyMoney() { get listDailyMoney() {
@ -40,7 +43,7 @@ export class Bill {
R.sum, R.sum,
R.map((bill: IBill) => bill.money) R.map((bill: IBill) => bill.money)
) )
return functions(this.bills) return functions(this._bills)
} }
get meanMoneyByDate() { get meanMoneyByDate() {
@ -54,14 +57,14 @@ export class Bill {
const {id} = await createBill(bill) const {id} = await createBill(bill)
bill.id = id bill.id = id
runInAction(() => { runInAction(() => {
this.bills.push(bill); this._bills.push(bill);
}) })
} }
async fetch(year: number, month: number) { async fetch(year: number, month: number) {
const data = await getBills(year, month) const data = await getBills(year, month)
runInAction(() => { runInAction(() => {
this.bills = data this._bills = data
}) })
} }