feat: 招商银行 PDF 账单导入、跨格式去重与导入性能优化

- 新增招商银行「交易流水」PDF 解析(PDFBox 坐标还原,支持多页与折行),CSV/PDF 去重标识统一,支持跨格式去重
- 账单导入改为批量插入,MyBatis 日志改 Slf4j,导入接口超时放宽至 120s
- 交易导出 Excel 增加样式、冻结表头与自动筛选,导出前二次确认
- 交易列表展示不计入统计金额,导入预览支持设置「计入统计」
This commit is contained in:
2026-09-19 14:30:09 +08:00
parent 9981e77bf1
commit 196afb682d
16 changed files with 1072 additions and 107 deletions
+35 -1
View File
@@ -66,7 +66,7 @@ const allowedExtensions: Record<BillType, string[]> = {
ALIPAY: ['.csv', '.zip'],
WECHAT: ['.xlsx', '.zip'],
MEITUAN: ['.csv', '.zip'],
CMB: ['.csv', '.zip'],
CMB: ['.csv', '.pdf', '.zip'],
BOCOM: ['.xls', '.zip'],
}
@@ -221,6 +221,18 @@ const columns = computed<DataTableColumns<ParsedBillRow>>(() => [
)
},
},
{
title: t('import.column.includeInStats'),
key: 'includeInStats',
width: 100,
align: 'center',
render: (row) =>
h(NCheckbox, {
checked: !!row.includeInStats,
disabled: !row.importable,
onUpdateChecked: (v: boolean) => toggleIncludeInStats(row, v),
}),
},
{
title: t('import.column.amount'),
key: 'amount',
@@ -389,6 +401,22 @@ function toggleAll(checked: boolean) {
checkedRowNos.value = checked ? selectableRows.value.map((r) => r.rowNo) : []
}
/** 切换单行是否计入统计 */
function toggleIncludeInStats(row: ParsedBillRow, value: boolean) {
row.includeInStats = value
}
/** 批量设置已勾选行的计入统计标记 */
function setCheckedIncludeInStats(value: boolean) {
if (checkedRows.value.length === 0) {
message.warning(t('import.selectAtLeastOne'))
return
}
checkedRows.value.forEach((row) => {
row.includeInStats = value
})
}
/** 确认导入 */
async function handleConfirm() {
if (checkedRows.value.length === 0) {
@@ -544,6 +572,12 @@ onMounted(() => {
>
{{ t('import.selectAll') }}
</NCheckbox>
<NButton size="small" :disabled="checkedRows.length === 0" @click="setCheckedIncludeInStats(true)">
{{ t('import.setIncluded') }}
</NButton>
<NButton size="small" :disabled="checkedRows.length === 0" @click="setCheckedIncludeInStats(false)">
{{ t('import.setExcluded') }}
</NButton>
<NText depth="3">{{ t('import.previewSummary', { total: rows.length, filtered: filteredRows.length, checked: checkedRows.length }) }}</NText>
</NSpace>