feat🔫: bill表格
This commit is contained in:
parent
6b54a5dc9d
commit
d7659d83b9
|
@ -17,6 +17,7 @@
|
||||||
.content {
|
.content {
|
||||||
flex: 1 1;
|
flex: 1 1;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
// }
|
// }
|
||||||
}
|
}
|
|
@ -1,2 +1,14 @@
|
||||||
.record {
|
.record {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new {
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
flex: 1 1;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user