Files
hzdyjs/web/src/views/cw/budgetExecutionImport/index.vue
T

255 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div>
<div class="page-actions" style="margin-bottom: 12px;">
<el-button type="primary" @click="triggerFileInput">导入</el-button>
</div>
<!-- Hidden native file input fallback to ensure upload works reliably -->
<input ref="fileInputRef" type="file" accept=".xlsx,.xls" style="display:none" @change="handleLocalFileChange" />
<import-dialog
v-if="false"
:visible="importDialogVisible"
:uploadUrl="importUrl"
:templateUrl="templateUrl"
@update:visible="importDialogVisible = $event"
@success="onImportSuccess"
/>
<el-card>
<div style="margin-bottom: 8px;">
<span>已选择批次:</span>
<el-select v-model="selectedBatchId" placeholder="请选择批次" @change="onBatchChange" style="min-width: 220px;">
<el-option
v-for="b in batches"
:key="b.id"
:label="b.batchNo + ' (' + b.createTime + ') - 合计:' + (b.totalAmount || 0)"
:value="String(b.id)"
/>
</el-select>
</div>
<!-- debug info removed -->
<div style="margin-bottom: 8px; font-weight: 600;">
总合计金额:{{ totalAmountDisplay }}
</div>
<div class="table-wrapper">
<el-table :data="items" border style="width: 100%; min-width: 1400px;">
<el-table-column prop="rowNo" label="序号" width="80" />
<el-table-column prop="serialNo" label="序号(原表)" width="120" />
<el-table-column prop="purpose" label="用途" min-width="180" />
<el-table-column prop="amount" label="金额(元)" width="140" />
<el-table-column prop="deptPaymentCategory" label="部门支出款项分类" min-width="160" />
<el-table-column prop="funcSubject" label="支出功能分类科目" min-width="160" />
<el-table-column prop="fundNature" label="资金性质" width="120" />
<el-table-column prop="indicatorSource" label="指标来源" min-width="140" />
<el-table-column prop="paymentType" label="支付类型" width="120" />
<el-table-column prop="payeeBank" label="收款人开户银行" min-width="180" />
<el-table-column prop="payee" label="收款人" width="160" />
<el-table-column prop="payeeAccount" label="收款账号" width="180" />
<el-table-column prop="projectType" label="项目类型" min-width="160" />
<el-table-column prop="project" label="项目" min-width="200" />
<el-table-column prop="receiptDate" label="回单时间" width="140" />
<el-table-column prop="localIndicatorDocNo" label="本级指标文号" min-width="160" />
<el-table-column prop="budgetSource" label="预算来源" min-width="140" />
<el-table-column prop="indicatorId" label="指标ID" width="160" />
<el-table-column prop="extField1" label="冗余字段1" width="140" />
<el-table-column prop="extField2" label="冗余字段2" width="140" />
<el-table-column prop="extField3" label="冗余字段3" width="140" />
<el-table-column prop="extField4" label="冗余字段4" width="140" />
<el-table-column prop="extField5" label="冗余字段5" width="140" />
<el-table-column prop="otherFields" label="扩展字段" min-width="200" />
</el-table>
</div>
<div style="margin-top: 12px; text-align: right;">
<el-pagination
background
layout="prev, pager, next, jumper"
:total="itemsTotal"
:page-size="pageSize"
:current-page.sync="pageNum"
@current-change="fetchItems"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed, watch, nextTick } from 'vue';
import ImportDialog from '@/components/ImportDialog/index.vue';
import { listImportBatches, listImportItems, downloadBudgetExecutionTemplate } from '@/api/cw/budgetExecutionImport';
import { ElMessage } from 'element-plus';
import { importBudgetExecution } from '@/api/cw/budgetExecutionImport';
const importDialogVisible = ref(false);
const importUrl = import.meta.env.VITE_APP_BASE_API + '/cw/budgetExecutionImport/importData';
const templateUrl = import.meta.env.VITE_APP_BASE_API + '/cw/budgetExecutionImport/importTemplate';
const batches = ref<any[]>([]);
const selectedBatchId = ref<string | null>(null);
const items = ref<any[]>([]);
const itemsTotal = ref(0);
const pageNum = ref(1);
const pageSize = ref(20);
const totalAmountDisplay = computed(() => {
const b = batches.value.find((x) => String(x.id) === selectedBatchId.value);
return b ? (b.totalAmount || 0) : 0;
});
const importUrlLocal = importUrl;
const openImport = () => {
importDialogVisible.value = true;
};
const downloadTemplate = async () => {
try {
await downloadBudgetExecutionTemplate();
// browser will handle download
} catch (e) {
ElMessage.error('模板下载失败');
}
};
const fileInputRef = ref<HTMLInputElement | null>(null);
const triggerFileInput = () => {
fileInputRef.value?.click();
};
const handleLocalFileChange = async (e: Event) => {
const input = e.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) {
ElMessage.warning('请先选择文件');
return;
}
// simple client-side validation
const allowedTypes = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel'];
if (file.size / 1024 / 1024 > 10) {
ElMessage.error('文件大小不能超过 10MB');
input.value = '';
return;
}
const formData = new FormData();
formData.append('file', file);
try {
// 使用对象形式调用以兼容不同环境下的实现细节,避免 createMessage 内部错误
ElMessage({ message: '正在导入,请稍候...', type: 'info', duration: 0 });
const res: any = await importBudgetExecution(file);
ElMessage.closeAll();
if (res && res.code === 200) {
ElMessage.success('导入成功');
fetchBatches();
if (selectedBatchId.value) {
fetchItems();
}
} else {
ElMessage.error(res?.msg || '导入失败');
}
} catch (err) {
ElMessage.closeAll();
console.error(err);
ElMessage.error('导入失败,请检查文件格式或网络');
} finally {
input.value = '';
}
};
const fetchBatches = async () => {
try {
const res: any = await listImportBatches({ pageNum: 1, pageSize: 100 });
console.debug('budgetExecutionImport - batch list raw res:', res);
let rows = [];
if (res) {
// response can be { code, msg, data: { rows } } or { total, rows, code }
rows = res.data?.rows ?? res.rows ?? res.data ?? [];
}
batches.value = Array.isArray(rows) ? rows : [];
console.debug('budgetExecutionImport - fetched batches:', batches.value);
} catch (e) {
console.error('fetchBatches error', e);
batches.value = [];
}
};
// 监听 batches 变化:如果未选择批次则自动选中第一个并加载明细
watch(
() => batches.value,
async (val) => {
if (val && val.length > 0 && !selectedBatchId.value) {
// 等待 DOM 更新再触发
selectedBatchId.value = String(val[0].id);
await nextTick();
console.debug('budgetExecutionImport - auto selected batchId:', selectedBatchId.value);
fetchItems(1);
}
},
{ immediate: true }
);
// no debugInfo in production
const fetchItems = async (page = pageNum.value) => {
try {
if (!selectedBatchId.value) {
items.value = [];
itemsTotal.value = 0;
return;
}
// Important: pass batchId as string to avoid JS Number precision loss for long IDs
const res: any = await listImportItems(selectedBatchId.value as string, { pageNum: page, pageSize: pageSize.value });
console.debug('budgetExecutionImport - item list raw res:', res);
let rows: any[] = [];
let totalCount = 0;
if (res) {
// support multiple formats: { code, data:{ rows, total } } or { total, rows, code }
rows = res.data?.rows ?? res.rows ?? res.data ?? [];
totalCount = res.data?.total ?? res.total ?? (Array.isArray(rows) ? rows.length : 0);
}
items.value = Array.isArray(rows) ? rows : [];
itemsTotal.value = totalCount || 0;
} catch (e) {
console.error('fetchItems error', e);
items.value = [];
itemsTotal.value = 0;
}
};
const onBatchChange = () => {
pageNum.value = 1;
fetchItems(1);
};
const onImportSuccess = () => {
importDialogVisible.value = false;
fetchBatches();
if (selectedBatchId.value) {
fetchItems();
}
ElMessage.success('导入成功');
};
onMounted(() => {
fetchBatches();
});
defineExpose({});
</script>
<style scoped>
.page-actions {
display: flex;
gap: 8px;
}
.table-wrapper {
overflow-x: auto;
}
</style>