小程序导出账单接口

This commit is contained in:
liu
2026-08-14 20:28:07 +08:00
parent 9a0999742f
commit 398bb98356
9 changed files with 693 additions and 4 deletions
+10
View File
@@ -1,4 +1,5 @@
import createAxios from '@/utils/request';
import { downloadBlob } from '@/api/common/download.ts';
import type { IBillDetail } from '@/domain/iBill.ts';
/** 账单详情(账单信息 + 合并商品明细 + 关联订单) */
@@ -17,3 +18,12 @@ export async function payBill(id: number, data: { paid_at: string; pay_remark?:
data,
});
}
/** 合并导出账单:勾选账单跨账单按商品合并明细,可按一级分类过滤 */
export async function exportBills(ids: number[], categoryId: number) {
return downloadBlob(
'/recon/bill/export',
{ ids: ids.join(','), category_id: categoryId },
'门店账单.xlsx'
);
}
+78 -3
View File
@@ -8,12 +8,13 @@ import {
Input,
message,
Modal,
Select,
Table,
Tag,
Typography,
} from 'antd';
import type { TableProps } from 'antd';
import { UnorderedListOutlined } from '@ant-design/icons';
import { DownloadOutlined, UnorderedListOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import XinTable from '@/components/XinTable';
import type {
@@ -24,8 +25,9 @@ import type {
import type { IBill, IBillDetail, IBillGoodsItem, IBillOrder } from '@/domain/iBill.ts';
import { BILL_STATUS_MAP } from '@/domain/iBill.ts';
import { STORE_ORDER_STATUS_MAP } from '@/domain/iStoreOrder.ts';
import { getBillDetail, payBill } from '@/api/recon/bill.ts';
import { exportBills, getBillDetail, payBill } from '@/api/recon/bill.ts';
import { getStoreOptions } from '@/api/customer/store.ts';
import { getCategoryTree } from '@/api/product/category.ts';
import type IStore from '@/domain/iStore.ts';
import AuthButton from '@/components/AuthButton';
@@ -53,8 +55,20 @@ const BillPage: React.FC = () => {
const [paySaving, setPaySaving] = useState(false);
const [payForm] = Form.useForm<PayFormValues>();
// 合并导出(勾选账单跨账单按商品合并明细,可按一级分类过滤)
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);
const [exportOpen, setExportOpen] = useState(false);
const [exporting, setExporting] = useState(false);
const [categoryId, setCategoryId] = useState(0);
const [topCategories, setTopCategories] = useState<{ value: number; label: string }[]>([]);
useEffect(() => {
getStoreOptions().then((res) => setStores(res.data.data ?? []));
getCategoryTree().then((res) =>
setTopCategories(
(res.data.data ?? []).map((c) => ({ value: c.id!, label: c.name ?? '' })),
),
);
}, []);
const openDetail = async (id: number) => {
@@ -93,6 +107,20 @@ const BillPage: React.FC = () => {
}
};
/** 提交合并导出:下载失败时 downloadBlob 内部已提示,保留勾选便于重试 */
const handleExport = async () => {
if (selectedRowKeys.length === 0) {
return;
}
setExporting(true);
try {
await exportBills(selectedRowKeys, categoryId);
setExportOpen(false);
} finally {
setExporting(false);
}
};
/** 合并商品明细列:品名/包规/单位/单价(加权平均)/数量/重量/金额 */
const itemColumns: TableProps<IBillGoodsItem>['columns'] = [
{ title: '品名', dataIndex: 'product_name', width: 160, align: 'center' },
@@ -306,6 +334,26 @@ const BillPage: React.FC = () => {
operateRender,
formProps: false,
actionBarRender: (dom) => [dom.search, dom.keywordSearch],
rowSelection: {
selectedRowKeys,
onChange: (keys) => setSelectedRowKeys(keys.map(Number)),
preserveSelectedRowKeys: true,
},
toolBarRender: (dom) => [
<AuthButton key="export" auth="recon.bill.export">
<Button
icon={<DownloadOutlined />}
disabled={selectedRowKeys.length === 0}
onClick={() => setExportOpen(true)}
>
{selectedRowKeys.length > 0 ? `${selectedRowKeys.length}` : ''}
</Button>
</AuthButton>,
dom.columnSetting,
dom.hideBorder,
dom.reload,
dom.columnHeight,
],
};
return (
@@ -313,7 +361,8 @@ const BillPage: React.FC = () => {
<div className="mb-5">
<Title level={3}></Title>
<Text type="secondary">
= + + /
= + + /
Excel
</Text>
</div>
<XinTable<IBill> {...tableProps} />
@@ -415,6 +464,32 @@ const BillPage: React.FC = () => {
</Form.Item>
</Form>
</Modal>
{/* 合并导出:勾选账单跨账单按商品合并明细,可按一级分类过滤 */}
<Modal
title="导出账单"
open={exportOpen}
onCancel={() => setExportOpen(false)}
onOk={handleExport}
confirmLoading={exporting}
okText="导出"
destroyOnHidden
>
<div className="py-2 text-gray-500">
<Text strong>{selectedRowKeys.length}</Text>
Excel
/
</div>
<Form layout="vertical">
<Form.Item label="商品分类">
<Select
value={categoryId}
onChange={(value) => setCategoryId(value)}
options={[{ value: 0, label: '全部分类' }, ...topCategories]}
/>
</Form.Item>
</Form>
</Modal>
</>
);
};