feat: 支持ZIP压缩包上传(含密码保护)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "web",
|
||||
"private": true,
|
||||
"version": "1.0.9",
|
||||
"version": "1.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -100,7 +100,7 @@ export interface MonthlyStatsResponse {
|
||||
export async function uploadBill(
|
||||
file: File,
|
||||
type: BillType,
|
||||
options?: { year?: number; month?: number }
|
||||
options?: { year?: number; month?: number; password?: string }
|
||||
): Promise<UploadResponse> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
@@ -112,6 +112,9 @@ export async function uploadBill(
|
||||
if (options?.month) {
|
||||
formData.append('month', options.month.toString());
|
||||
}
|
||||
if (options?.password) {
|
||||
formData.append('password', options.password);
|
||||
}
|
||||
|
||||
const response = await apiFetch(`${API_BASE}/api/upload`, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
let isUploading = $state(false);
|
||||
let uploadResult: UploadResponse | null = $state(null);
|
||||
let errorMessage = $state('');
|
||||
let zipPassword = $state('');
|
||||
let isZipFile = $state(false);
|
||||
|
||||
type StatTrend = 'up' | 'down';
|
||||
interface StatCard {
|
||||
@@ -186,16 +188,27 @@
|
||||
}
|
||||
|
||||
function selectFile(file: File) {
|
||||
if (!file.name.endsWith('.csv')) {
|
||||
errorMessage = '请选择 CSV 格式的账单文件';
|
||||
const fileName = file.name.toLowerCase();
|
||||
const isZip = fileName.endsWith('.zip');
|
||||
const isCsv = fileName.endsWith('.csv');
|
||||
const isXlsx = fileName.endsWith('.xlsx');
|
||||
|
||||
if (!isCsv && !isZip && !isXlsx) {
|
||||
errorMessage = '请选择 CSV、XLSX 或 ZIP 格式的账单文件';
|
||||
return;
|
||||
}
|
||||
|
||||
selectedFile = file;
|
||||
isZipFile = isZip;
|
||||
errorMessage = '';
|
||||
uploadResult = null;
|
||||
|
||||
// 如果不是 ZIP 文件,清空密码
|
||||
if (!isZip) {
|
||||
zipPassword = '';
|
||||
}
|
||||
|
||||
// 根据文件名自动识别账单类型
|
||||
const fileName = file.name.toLowerCase();
|
||||
if (fileName.includes('支付宝') || fileName.includes('alipay')) {
|
||||
selectedType = 'alipay';
|
||||
} else if (fileName.includes('微信') || fileName.includes('wechat')) {
|
||||
@@ -207,6 +220,8 @@
|
||||
selectedFile = null;
|
||||
uploadResult = null;
|
||||
errorMessage = '';
|
||||
zipPassword = '';
|
||||
isZipFile = false;
|
||||
}
|
||||
|
||||
async function handleUpload() {
|
||||
@@ -216,7 +231,11 @@
|
||||
errorMessage = '';
|
||||
|
||||
try {
|
||||
const result = await uploadBill(selectedFile, selectedType);
|
||||
const options: { year?: number; month?: number; password?: string } = {};
|
||||
if (isZipFile && zipPassword) {
|
||||
options.password = zipPassword;
|
||||
}
|
||||
const result = await uploadBill(selectedFile, selectedType, options);
|
||||
if (result.result) {
|
||||
uploadResult = result;
|
||||
} else {
|
||||
@@ -278,7 +297,7 @@
|
||||
<Card.Header class="flex flex-row items-center justify-between space-y-0">
|
||||
<div>
|
||||
<Card.Title>上传账单</Card.Title>
|
||||
<Card.Description>支持支付宝、微信账单 CSV 文件</Card.Description>
|
||||
<Card.Description>支持支付宝、微信账单 CSV、XLSX 或 ZIP 文件</Card.Description>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" onclick={() => goto('/bills?tab=manual')}>
|
||||
<Plus class="mr-2 h-4 w-4" />
|
||||
@@ -301,7 +320,7 @@
|
||||
<input
|
||||
type="file"
|
||||
id="file-input"
|
||||
accept=".csv"
|
||||
accept=".csv,.xlsx,.zip"
|
||||
onchange={handleFileSelect}
|
||||
hidden
|
||||
/>
|
||||
@@ -333,7 +352,7 @@
|
||||
<p class="font-medium">
|
||||
{isDragOver ? '松开鼠标上传文件' : '拖拽文件到这里,或点击选择'}
|
||||
</p>
|
||||
<p class="text-sm text-muted-foreground">支持 .csv 格式</p>
|
||||
<p class="text-sm text-muted-foreground">支持 .csv、.xlsx、.zip 格式</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -347,6 +366,19 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- ZIP 密码输入 -->
|
||||
{#if isZipFile}
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-sm font-medium">ZIP 密码:</span>
|
||||
<input
|
||||
type="password"
|
||||
bind:value={zipPassword}
|
||||
placeholder="如有密码请输入"
|
||||
class="flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- 账单类型选择 -->
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-sm font-medium">账单类型:</span>
|
||||
|
||||
Reference in New Issue
Block a user