From 9c6fbd839d3e22f32aa35f6519cde395d7457c63 Mon Sep 17 00:00:00 2001 From: callmeyan Date: Thu, 8 Aug 2024 20:24:10 +0800 Subject: [PATCH] feat: add table column diy --- src/components/bill/list.tsx | 611 +++++++++++++++++++---------------- 1 file changed, 340 insertions(+), 271 deletions(-) diff --git a/src/components/bill/list.tsx b/src/components/bill/list.tsx index 7c311fc..3f50200 100644 --- a/src/components/bill/list.tsx +++ b/src/components/bill/list.tsx @@ -1,296 +1,365 @@ -import {Space, Table, Typography} from "@douyinfe/semi-ui"; +import {Button, Checkbox, CheckboxGroup, Space, Table, Typography} from "@douyinfe/semi-ui"; import {ColumnProps} from "@douyinfe/semi-ui/lib/es/table"; -import React, {useMemo, useState} from "react"; +import React, {ReactNode, useMemo, useState} from "react"; import {useTranslation} from "react-i18next"; import dayjs from "dayjs"; -import {IconCheckCircleStroked} from "@douyinfe/semi-icons"; +import {IconCheckCircleStroked, IconSetting} from "@douyinfe/semi-icons"; import MoneyFormat from "@/components/money-format.tsx"; import {Card} from "@/components/card"; import './bill.less' import {BillStatus} from "@/service/types.ts"; +import {useSetState} from "ahooks"; +import {clone} from "lodash"; type BillListProps = { - type: 'query' | 'reconciliation'; - operationRender?: (record: BillModel) => React.ReactNode; - operationRenderWidth?: number; - onRowSelection?: (selectedRowKeys: (string | number)[]) => void; - source?: RecordList; - onPageChange: (pageIndex:number) => void; - tableFooter?: React.ReactNode; - loading?: boolean; - beforeTotalAmount?: React.ReactNode; + type: 'query' | 'reconciliation'; + operationRender?: (record: BillModel) => React.ReactNode; + operationRenderWidth?: number; + onRowSelection?: (selectedRowKeys: (string | number)[]) => void; + source?: RecordList; + onPageChange: (pageIndex: number) => void; + tableFooter?: React.ReactNode; + loading?: boolean; + beforeTotalAmount?: React.ReactNode; } -const CheckNumberCorrect = ({origin,confirmed}:{origin: string,confirmed?:string}) => { - if(origin == confirmed && origin){ - return ({origin}) - } - return
-
{origin?.length ?origin: 'N/A'}
- {confirmed&&{confirmed}} -
+const CheckNumberCorrect = ({origin, confirmed}: { origin: string, confirmed?: string }) => { + if (origin == confirmed && origin) { + return ({origin}) + } + return
+
{origin?.length ? origin : 'N/A'}
+ {confirmed && + {confirmed}} +
} export const BillList: React.FC = (props) => { - const {t, i18n} = useTranslation() - const [currentTotalAmount,setCurrentTotalAmount] = useState(0) + const {t, i18n} = useTranslation() + const [currentTotalAmount, setCurrentTotalAmount] = useState(0) + const [state, setState] = useSetState<{ + showColumnsConfig?: boolean; + showCols: string[] + }>({ + showCols: [ + "id", "merchant_ref", "student_number", "application_number", "initiated_paid_at", "delivered_at", "paid_at", "student_english_name", "student_email", "programme_chinese_name", + "intake_year", "detail", "_detail", "amount", "pay_amount", "actual_payment_amount", "pay_method", "status", "apply_status" + ] + }) + const billStatusText = (billStatus: string) => { + switch (billStatus) { + case 'PENDING': + return t('bill.pay_status_pending') + case 'PAID': + return t('bill.pay_status_paid') + case 'EXPIRED': + return t('bill.pay_status_expired') + case 'CANCELED': + case 'CANCELLED': + return t('bill.pay_status_canceled') + default: + return billStatus + } + } + const applyStatusText = (status: string) => { + switch (status) { + case 'UNCHECKED': + return t('bill.reconciliation_status_pending') + case 'CHECKED': + return t('bill.reconciliation_status_submitted') + default: + return status + } + } - const billStatusText = (billStatus: string) => { - switch (billStatus) { - case 'PENDING': - return t('bill.pay_status_pending') - case 'PAID': - return t('bill.pay_status_paid') - case 'EXPIRED': - return t('bill.pay_status_expired') - case 'CANCELED': - case 'CANCELLED': - return t('bill.pay_status_canceled') - default: - return billStatus - } - } - const applyStatusText = (status:string) => { - switch (status) { - case 'UNCHECKED': - return t('bill.reconciliation_status_pending') - case 'CHECKED': - return t('bill.reconciliation_status_submitted') - default: - return status - } - } + const allCols = useMemo[]>(() => { + const cols: ColumnProps[] = [ + { + title: '#ID', + dataIndex: 'id', + width: 120, + }, + { + title: 'Merchant Ref', + dataIndex: 'merchant_ref', + width: 200, + // render: (_) => (), + }, + { + title: t('base.student_number'), + dataIndex: 'student_number', + width: 150, + render: (value, record) => ( + ) + }, + { + title: t('base.bill_number'), + dataIndex: 'application_number', + width: 150, + render: (value, record) => ( + ) + }, + { + title:
{t('bill.title_initiated_paid_at')} +
(PPS Input Date)
+
, + dataIndex: 'initiated_paid_at', + width: 180, + render: (value) => value?.length ? value : 'N/A' + }, + { + title:
{t('bill.title_delivered_at')} +
(PPS Statement Date)
+
, + dataIndex: 'delivered_at', + width: 180, + render: (value) => value?.length ? value : 'N/A' + }, + { + title: t('bill.title_paid_at'), + dataIndex: 'paid_at', + width: 180, + render: (value) => value?.length ? value : 'N/A' + }, + // { + // title: t('bill.title_create_at'), + // dataIndex: 'create_at', + // width: 180, + // render: (value) => value?.length ?value: 'N/A' + // }, + { + title: t('bill.title_student_name'), + dataIndex: 'student_english_name', + width: 180, + render: (_, record) => (
{record.student_english_name}
{record.student_chinese_name}
) + }, + { + title: 'Email', + dataIndex: 'student_email', + width: 200, + render: (value) => value?.length ? value : 'N/A' + // render: (_, record) => (
{record.student_english_name}
{record.student_chinese_name}
) + }, + { + title: t('bill.title_program_name'), + width: 250, + dataIndex: i18n.language == 'en-US' ? 'programme_english_name' : 'programme_chinese_name', + }, + { + title: t('bill.title_department'), + width: 200, + dataIndex: '', + render: (_, record) => (i18n.language == 'en-US' ? record.department_english_name : record.department_chinese_name), + }, + { + title: t('bill.title_year'), + dataIndex: 'intake_year', + width: 120, + render: (_, record) => record.intake_year ? ( +
{record.intake_year}/{String(record.intake_semester).length == 1 ? '0' : ''}{record.intake_semester}
) : "N/A" + }, + // { + // title: t('bill.title_semester'), + // dataIndex: 'intake_semester', + // width: 120, + // }, + { + title: t('bill.title_bill_detail'), + dataIndex: 'detail', + ellipsis: {showTitle: true}, + width: 220, + render: (_, record) => (
+ {record.details.map((it, idx) => ( +
{it.bill_type}:
))} +
), + }, + { + title: t('bill.title_bill_type_confirm'), + dataIndex: '_detail', + ellipsis: {showTitle: true}, + width: 220, + render: (_, record) => (
+ {record.details.filter(s => s.confirm_status == 'CONFIRMED').map((it) => ( +
{it.confirm_type}:
))} +
), + }, + { + title: t('bill.title_amount'), + dataIndex: 'amount', + width: 150, + render: (_) => (), + }, + { + title: t('bill.title_pay_amount'), + dataIndex: 'pay_amount', + width: 190, + render: (_, record) => { - const columns = useMemo[]>(() => { - const cols: ColumnProps[] = [ - { - title: '#ID', - dataIndex: 'id', - width: 120, - }, - { - title: 'Merchant Ref', - dataIndex: 'merchant_ref', - width: 200, - // render: (_) => (), - }, - { - title: t('base.student_number'), - dataIndex: 'student_number', - width: 150, - render: (value,record) => () - }, - { - title: t('base.bill_number'), - dataIndex: 'application_number', - width: 150, - render: (value,record) => () - }, - { - title: t('bill.title_initiated_paid_at'), - dataIndex: 'initiated_paid_at', - width: 180, - render: (value) => value?.length ?value: 'N/A' - }, - { - title: t('bill.title_delivered_at'), - dataIndex: 'delivered_at', - width: 180, - render: (value) => value?.length ?value: 'N/A' - }, - { - title: t('bill.title_paid_at'), - dataIndex: 'paid_at', - width: 180, - render: (value) => value?.length ?value: 'N/A' - }, - { - title: t('bill.title_create_at'), - dataIndex: 'create_at', - width: 180, - render: (value) => value?.length ?value: 'N/A' - }, - { - title: t('bill.title_student_name'), - dataIndex: 'student_english_name', - width: 180, - render: (_, record) => (
{record.student_english_name}
{record.student_chinese_name}
) - }, - { - title: 'Email', - dataIndex: 'student_email', - width: 200, - render: (value) => value?.length ?value: 'N/A' - // render: (_, record) => (
{record.student_english_name}
{record.student_chinese_name}
) - }, - { - title: t('bill.title_program_name'), - width: 250, - dataIndex: i18n.language == 'en-US' ? 'programme_english_name' : 'programme_chinese_name', - }, - { - title: t('bill.title_department'), - width: 200, - dataIndex: i18n.language == 'en-US' ? 'department_english_name' : 'department_chinese_name', - }, - { - title: t('bill.title_year'), - dataIndex: 'intake_year', - width: 120, - render: (_, record) => record.intake_year?(
{record.intake_year}/{String(record.intake_semester).length == 1 ? '0':''}{record.intake_semester}
):"N/A" - }, - // { - // title: t('bill.title_semester'), - // dataIndex: 'intake_semester', - // width: 120, - // }, - { - title: t('bill.title_bill_detail'), - dataIndex: 'detail', - ellipsis: {showTitle: true}, - width: 220, - render: (_, record) => (
- {record.details.map((it, idx) => (
{it.bill_type}:
))} -
), - }, - { - title: t('bill.title_bill_type_confirm'), - dataIndex: '_detail', - ellipsis: {showTitle: true}, - width: 220, - render: (_, record) => (
- {record.details.filter(s=>s.confirm_status == 'CONFIRMED').map((it) => (
{it.confirm_type}:
))} -
), - }, - { - title: t('bill.title_amount'), - dataIndex: 'amount', - width: 150, - render: (_) => (), - }, - { - title: t('bill.title_pay_amount'), - dataIndex: 'pay_amount', - width: 190, - render: (_, record) => { - - if (record.service_charge && record.service_charge > 0) { - return
-
- - {t('bill.title_service_charge')}: - -
- } - return (
) - }, - }, - { - title: t('bill.title_actual_payment_amount'), - dataIndex: 'actual_payment_amount', - width: 150, - render: (_,record) => (), - }, - { - title: t('bill.title_pay_method'), - dataIndex: 'pay_method', - width: 130, - render: (_, {payment_method, payment_channel}) => (payment_channel?( -
- {payment_channel} - {payment_method && payment_method.length > 0 &&
+ if (record.service_charge && record.service_charge > 0) { + return
+
+ + {t('bill.title_service_charge')}: + +
+ } + return (
) + }, + }, + { + title: t('bill.title_actual_payment_amount'), + dataIndex: 'actual_payment_amount', + width: 150, + render: (_, record) => (), + }, + { + title: t('bill.title_pay_method'), + dataIndex: 'pay_method', + width: 130, + render: (_, {payment_method, payment_channel}) => (payment_channel ? ( +
+ {payment_channel} + {payment_method && payment_method.length > 0 &&
({payment_method})
} -
- ):'N/A'), - }, - { - title: t('bill.title_bill_status'), - dataIndex: 'status', - width: 150, - render: value => billStatusText(value), - }, - ] - if (props.type != 'reconciliation') { - cols.push({ - title: t('bill.title_reconciliation_status'), - dataIndex: 'apply_status', - width: 150, - render: value => applyStatusText(value), - }) - } - if (props.operationRender) { - cols.push({ - title: t('bill.title_operate'), - dataIndex: 'operate', - fixed: 'right', - width: props.operationRenderWidth || (props.type == 'reconciliation'?120:220), - render: (_, record) => props.operationRender?.(record), - }) - } - return cols; - }, [props.operationRender, props.type, i18n.language]); +
+ ) : 'N/A'), + }, + { + title: t('bill.title_bill_status'), + dataIndex: 'status', + width: 150, + render: value => billStatusText(value), + }, + ] + if (props.type != 'reconciliation') { + cols.push({ + title: t('bill.title_reconciliation_status'), + dataIndex: 'apply_status', + width: 150, + render: value => applyStatusText(value), + }) + } + return cols; + }, [props.type, i18n.language]) - const isExpired = (bill: BillModel) => { - return bill.status == BillStatus.PENDING && dayjs(bill.expiration_time).isBefore(Date.now()) - } - const currentList = useMemo(()=>{ - const originList = props.source?.list || []; - originList.forEach(s => { - if(isExpired(s)){ - s.status = BillStatus.EXPIRED; - } - }) - const _total = originList.map(s=>Number(s.amount)).reduce((s, c) => (s + c), 0) - setCurrentTotalAmount(_total) - return originList; - },[props.source]) + const columns = useMemo[]>(() => { + const _cols = clone(allCols); + const cols = state.showCols.length == 0 ? _cols : _cols.filter((it: ColumnProps) => !it.dataIndex || state.showCols.includes(it.dataIndex)) + if (props.operationRender) { + cols.push({ + title: t('bill.title_operate'), + dataIndex: 'operate', + fixed: 'right', + width: props.operationRenderWidth || (props.type == 'reconciliation' ? 120 : 220), + render: (_, record) => props.operationRender?.(record), + }) + } + return cols; + }, [props.operationRender, props.type, i18n.language, allCols, state.showCols]); - return - {props.beforeTotalAmount} -
-
- {t('bill.query_amount_total')} : - -
-
- {t('bill.query_amount_current_page')} : - -
+ const isExpired = (bill: BillModel) => { + return bill.status == BillStatus.PENDING && dayjs(bill.expiration_time).isBefore(Date.now()) + } + + const currentList = useMemo(() => { + const originList = props.source?.list || []; + originList.forEach(s => { + if (isExpired(s)) { + s.status = BillStatus.EXPIRED; + } + }) + const _total = originList.map(s => Number(s.amount)).reduce((s, c) => (s + c), 0) + setCurrentTotalAmount(_total) + return originList; + }, [props.source]) + + return + {t('bill.title_bill_list')} + setState({showColumnsConfig: true})}> + + + } + headerRight={ + {props.beforeTotalAmount} +
+
+ {t('bill.query_amount_total')} : + +
+
+ {t('bill.query_amount_current_page')} : + +
+
+
} + > + {state.showColumnsConfig &&
+
+ setState({showCols})}> + {allCols.map((it) => { + return ({it.title as ReactNode}) + })} +
- } - > -
- - bordered - columns={columns} - dataSource={currentList} - rowKey={'id'} - pagination={{ - currentPage: props.source?.pagination.current, - pageSize: props.source?.pagination.pageSize, - total: props.source?.pagination.total, - onPageChange: props.onPageChange, - formatPageText: (params) => ( -
- {props.tableFooter} - {props.source && props.source.pagination.recordTotal > 0 && {t('page.record-show',params)}} -
- ) - }} - loading={props.loading} - rowSelection={props.onRowSelection ? { - fixed: true, - onChange: (selectedRowKeys) => { - selectedRowKeys && props.onRowSelection?.(selectedRowKeys) - } - } : undefined} - /> -
- +
+ + + + +
+ +
} +
+ + bordered + columns={columns} + dataSource={currentList} + rowKey={'id'} + pagination={{ + currentPage: props.source?.pagination.current, + pageSize: props.source?.pagination.pageSize, + total: props.source?.pagination.total, + onPageChange: props.onPageChange, + formatPageText: (params) => ( +
+ {props.tableFooter} + {props.source && props.source.pagination.recordTotal > 0 && + {t('page.record-show', params)}} +
+ ) + }} + loading={props.loading} + rowSelection={props.onRowSelection ? { + fixed: true, + onChange: (selectedRowKeys) => { + selectedRowKeys && props.onRowSelection?.(selectedRowKeys) + } + } : undefined} + /> +
+
} \ No newline at end of file