Browse Source

评估、会计 发票批量收款功能

徐滕 2 days atrás
parent
commit
c30cfb5d5f

+ 26 - 0
src/api/cw/invoice/CwFinanceInvoiceService.js

@@ -211,4 +211,30 @@ export default {
 			data: data,
 		});
 	},
+
+	exportInvoiceTemplate () {
+		return request({
+			url: prefix + "/cw_finance/invoice/importCwFinance/template",
+			method: "get",
+			responseType: "blob",
+		});
+	},
+
+	importProjectRecords (data) {
+		return request({
+			url: prefix + "/cw_finance/invoice/importProjectRecords",
+			method: "post",
+			data: data,
+		});
+	},
+
+	// 下载导入结果Excel
+	downloadImportResult(fileName) {
+		return request({
+			url: prefix + "/cw_finance/invoice/downloadImportResult",
+			method: 'get',
+			params: { fileName: fileName },
+			responseType: 'blob'
+		})
+	},
 };

+ 26 - 0
src/api/finance/invoice/FinanceInvoiceService.js

@@ -132,4 +132,30 @@ export default class FinanceInvoiceService {
 			params: params,
 		});
 	}
+
+	exportInvoiceTemplate () {
+		return request({
+			url: prefix + "/finance/invoice/importFinance/template",
+			method: "get",
+			responseType: "blob",
+		});
+	}
+
+	importProjectRecords (data) {
+		return request({
+			url: prefix + "/finance/invoice/importProjectRecords",
+			method: "post",
+			data: data,
+		});
+	}
+
+	// 下载导入结果Excel
+	downloadImportResult(fileName) {
+		return request({
+			url: prefix + "/finance/invoice/downloadImportResult",
+			method: 'get',
+			params: { fileName: fileName },
+			responseType: 'blob'
+		})
+	}
 }

+ 42 - 1
src/views/cw/invoice/InvoiceList.vue

@@ -160,10 +160,14 @@
 						@click="batchInvoicePush()" plain
 						:disabled="$refs.invoiceTable && $refs.invoiceTable.getCheckboxRecords().length === 0">批量发起开票申请</el-button>
 
+					<el-button v-if="hasPermission('cw_finance:invoice:importInvoiceBatch')" type="primary"
+							   @click="projectImport()" plain>批量收款导入</el-button>
+
 					<el-switch v-if="hasPermission('cw_finance:invoice:dd_notice')" v-model="dingNoticeEnabled" inline-prompt
 						style="margin-left: 10px;--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
 						active-value="1" inactive-value="0" active-text="接收钉钉通知" inactive-text="不接收钉钉通知"
 						:before-change="beforeChangeDingNotice" @change="changeDingNotice" />
+
 				</template>
 				<template #tools>
 					<vxe-button text type="primary" :title="searchVisible ? '收起检索' : '展开检索'" icon="vxe-icon-search"
@@ -196,7 +200,7 @@
 							}}</el-link>
 						</template>
 					</vxe-column>
-					<vxe-column min-width="150" title="发票号" align="center" field="number"></vxe-column>
+					<vxe-column min-width="180" title="发票号" align="center" field="number"></vxe-column>
 					<vxe-column min-width="150" title="实际开票单位/个人" align="center"
 						field="billingWorkplaceReal"></vxe-column>
 					<vxe-column min-width="150" title="经办人" align="center" field="operator"></vxe-column>
@@ -320,6 +324,7 @@
 		<user-select1 ref="userSelect1" @doSubmit="selectUser1"></user-select1>
 		<user-select2 ref="userSelect2" @doSubmit="selectUser2"></user-select2>
 		<CwFinanceImport ref="cwFinanceImport" @refreshDataList="refreshList"></CwFinanceImport>
+		<InvoiceListImport ref="invoiceListImport" @refreshDataList="refreshList" @import-error="handleImportError"></InvoiceListImport>
 	</div>
 
 	<el-dialog title="详情信息" v-model="dialogFormVisible">
@@ -360,6 +365,7 @@ import UserSelect1 from '@/views/utils/UserTreeSelect'
 import UserSelect2 from '@/views/utils/UserTreeSelect'
 import CwFinanceImport from "./CwFinanceImport";
 import { ElMessageBox } from 'element-plus'
+import InvoiceListImport from './InvoiceListImport'
 export default {
 	data() {
 		return {
@@ -424,6 +430,7 @@ export default {
 	created() {
 	},
 	components: {
+		InvoiceListImport,
 		InvoiceForm,
 		UserSelect,
 		SelectTree,
@@ -451,6 +458,40 @@ export default {
 		this.refreshList()
 	},
 	methods: {
+		// 收款记录导入
+		projectImport() {
+			this.$refs.invoiceListImport.init()
+		},
+
+		handleImportError(data) {
+			console.log("父页面收到错误:", data);
+
+			// 用 async 包裹,彻底解决 ElementUI 弹框乱触发问题!
+			this.$nextTick(async () => {
+				try {
+					await this.$confirm('导入数据存在问题,是否下载错误详情文件?', '提示', {
+						confirmButtonText: '是',
+						cancelButtonText: '否',
+						type: 'warning',
+						distinguishCancelAndClose: true // 关键:区分取消和关闭
+					});
+
+					// ========== 点 【是】 才会走这里 ==========
+					console.log("用户确认下载,文件名:", data.excelFileName);
+
+					financeInvoiceService.downloadImportResult(data.excelFileName).then(res => {
+						this.$utils.downloadExcel(res, '发票导入错误结果.xlsx');
+						this.$message.success('文件下载成功!');
+					}).catch(err => {
+						this.$message.error('下载失败');
+					});
+
+				} catch (err) {
+					// ========== 点 【否】 或 关闭 走这里 ==========
+					this.$message.info('已取消下载');
+				}
+			})
+		},
 		showHide() {
 			if (this.showHideItem === false) {
 				this.showHideItem = true

+ 153 - 0
src/views/cw/invoice/InvoiceListImport.vue

@@ -0,0 +1,153 @@
+<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
+	<div>
+		<el-dialog :title="title" :close-on-click-modal="false" draggable width="1000px" height="500px" @close="close"
+			append-to-body v-model="visible">
+			<el-form :model="inputForm" ref="inputForm" :class="method === 'view' ? 'readonly' : ''"
+				:disabled="status === 'audit' || status === 'taskFormDetail'" label-width="135px"
+				@submit.native.prevent>
+				<el-row :gutter="0">
+				</el-row>
+				<el-row :gutter="0">
+					<el-col :span="24">
+						<el-form-item label="附件上传:">
+							<el-upload ref="upload" :on-remove="handleRemove" action="" :limit="1" :auto-upload="false"
+								:on-change="beforeUploadDetail" :show-file-list="true">
+								<el-button type="primary">点击上传Excel文件</el-button>
+							</el-upload>
+						</el-form-item>
+					</el-col>
+				</el-row>
+
+			</el-form>
+
+
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button type="warning" @click="downloadTpl()" plain>下载模板</el-button>
+					<el-button @click="close()" icon="el-icon-circle-close">关闭</el-button>
+					<el-button v-if="method !== 'view'" type="primary" icon="el-icon-circle-check"
+						@click="doSubmit()">确定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+
+import cwFinanceInvoiceService from '@/api/cw/invoice/CwFinanceInvoiceService'
+export default {
+	data() {
+		return {
+			visible: false,
+			title: '',
+			inputForm: {
+				file: []
+			}
+		}
+	},
+	created() {
+	},
+	components: {
+	},
+	methods: {
+		handleRemove() {
+			this.inputForm.file.pop()
+			console.log(this.inputForm.file);
+		},
+		init() {
+			this.visible = true
+			this.title = '收款发票导入'
+			this.inputForm = {
+				file: []
+			}
+			this.$forceUpdate()
+		},
+
+		beforeUploadDetail(file) {
+			this.inputForm.file.push(file)
+		},
+
+		// 下载模板
+		downloadTpl() {
+			const loading = this.$loading({
+				lock: true,
+				text: '模板下载中,请稍后',
+				spinner: 'el-icon-loading',
+				background: 'rgba(255, 255, 255, 0.3)'
+			});
+			cwFinanceInvoiceService.exportInvoiceTemplate().then((res) => {
+				// 将二进制流文件写入excel表,以下为重要步骤
+				this.$utils.downloadExcel(res, '收款发票批量导入模板' + ".xlsx")
+				loading.close();
+			}).catch(function (err) {
+				loading.close();
+				if (err.response) {
+					console.log(err.response)
+				}
+			})
+		},
+		doSubmit() {
+			if (this.inputForm.file.length == 0) {
+				this.$message.warning('未上传文件,无法提交导入')
+				return
+			}
+			const loading = this.$loading({
+				lock: true,
+				text: '导入中,请稍后',
+				spinner: 'el-icon-loading',
+				background: 'rgba(255, 255, 255, 0.3)'
+			});
+			const formBody = new FormData()
+			formBody.append('file', this.inputForm.file[0].raw)
+
+			cwFinanceInvoiceService.importProjectRecords(formBody).then((data) => {
+				loading.close();
+
+				console.log("后端返回数据:", data); // 调试看有没有返回
+				console.log("excelFileName:", data?.excelFileName);
+
+				// ==========================================
+				// 关键:只要有文件名,就强制触发父页面事件
+				// ==========================================
+				if (data && data.excelFileName) {
+					this.$emit('import-error', data);
+					this.$message.warning("导入存在异常,请下载查看");
+					this.close(); // ✅ 加这一行,子弹窗自动关闭,父页面弹框更清晰
+					return;
+				}
+
+				// 正常成功
+				this.$message.success('导入成功');
+				this.close();
+				this.$emit("refreshDataList");
+
+			}).catch(err => {
+				loading.close();
+				this.$message.error('导入失败:服务异常');
+			})
+		},
+		close() {
+			this.visible = false
+			this.$refs.inputForm.resetFields()
+			this.$refs.upload.clearFiles();
+			this.inputForm = {
+				file: []
+			}
+		}
+
+	}
+}
+</script>
+
+<style scoped>
+/deep/ .el-input-number .el-input__inner {
+	text-align: left;
+}
+
+.el-row {
+	display: flex;
+	align-items: center;
+	/* 垂直居中 */
+}
+</style>

+ 1 - 1
src/views/cw/projectReportArchive/ProjectReportArchiveList.vue

@@ -241,7 +241,7 @@
 					  v-if="hasPermission('cwProjectReportArchive:edit') && (scope.row.createBy.id === $store.state.user.id || (haveProjectIds.includes(scope.row.projectId) && haveWhereIsCpa.includes($store.state.user.id) )) && (scope.row.status === '0' || scope.row.status === '1' || scope.row.status === '3')"
                   text type="primary" @click="push(scope.row)">归档</el-button>-->
                 <el-button
-                  v-if="hasPermission('cwProjectReportArchive:edit') && ((scope.row.projectMaster === $store.state.user.id || scope.row.realHeader === $store.state.user.id || (scope.row.praaStatus === '5' && scope.row.projectExecutor === $store.state.user.id ))) && scope.row.status === '2'"
+                  v-if="hasPermission('cwProjectReportArchive:edit') && ((scope.row.projectMaster === $store.state.user.id || scope.row.realHeader === $store.state.user.id || scope.row.projectExecutorFlag || (scope.row.praaStatus === '5' && scope.row.projectExecutor === $store.state.user.id ))) && scope.row.status === '2'"
                   text type="primary" @click="reback(scope.row)">撤回</el-button>
                 <!--报告借用-->
                 <el-button

+ 49 - 1
src/views/finance/invoice/InvoiceList.vue

@@ -138,11 +138,19 @@
 					<el-button v-if="hasPermission('finance:invoice:add')" type="primary" icon="el-icon-plus"
 						@click="start()">新建</el-button>
 					<!--          <el-button v-if="hasPermission('finance:invoice:del')" type="danger"   size="small" icon="el-icon-delete" @click="del()" :disabled="$refs.invoiceTable && $refs.invoiceTable.getCheckboxRecords().length === 0" plain>删除</el-button>-->
+
+
+
+					<el-button v-if="hasPermission('finance:invoice:importInvoiceBatch')" type="primary"
+							   @click="projectImport()" plain>批量收款导入</el-button>
+
+
 					<el-switch v-if="hasPermission('cw_finance:invoice:dd_notice')" v-model="dingNoticeEnabled"
 						inline-prompt
 						style="margin-left: 10px;--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
 						active-value="1" inactive-value="0" active-text="接收钉钉通知" inactive-text="不接收钉钉通知"
 						:before-change="beforeChangeDingNotice" @change="changeDingNotice" />
+
 				</template>
 			</vxe-toolbar>
 			<div class="jp-table-body">
@@ -171,7 +179,7 @@
 								scope.row.no }}</el-link>
 						</template>
 					</vxe-column>
-					<vxe-column min-width="150" title="发票号" align="center" field="number"></vxe-column>
+					<vxe-column min-width="180" title="发票号" align="center" field="number"></vxe-column>
 					<vxe-column min-width="150" title="实际开票单位/个人" align="center"
 						field="billingWorkplaceReal"></vxe-column>
 					<vxe-column min-width="150" title="经办人" align="center" field="operator"></vxe-column>
@@ -276,6 +284,9 @@
 		<ProgramForm ref="programForm"></ProgramForm>
 		<user-select1 ref="userSelect1" @doSubmit="selectUser1"></user-select1>
 		<user-select2 ref="userSelect2" @doSubmit="selectUser2"></user-select2>
+		<InvoiceListImport ref="invoiceListImport" @refreshDataList="refreshList"></InvoiceListImport>
+		<InvoiceListImport ref="invoiceListImport" @refreshDataList="refreshList" @import-error="handleImportError"></InvoiceListImport>
+
 	</div>
 </template>
 
@@ -296,6 +307,7 @@ import ProgramPageForm from '@/views/finance/invoice/ProgramPageForm'
 import UserSelect1 from '@/views/utils/UserTreeSelect'
 import UserSelect2 from '@/views/utils/UserTreeSelect'
 import { ElMessageBox } from 'element-plus'
+import InvoiceListImport from './InvoiceListImport'
 export default {
 	data() {
 		return {
@@ -351,6 +363,7 @@ export default {
 		// this.userService = new UserService()
 	},
 	components: {
+		InvoiceListImport,
 		InvoiceForm,
 		UserSelect,
 		SelectUserTree,
@@ -375,6 +388,41 @@ export default {
 		this.refreshList()
 	},
 	methods: {
+		// 收款记录导入
+		projectImport() {
+			this.$refs.invoiceListImport.init()
+		},
+
+		handleImportError(data) {
+			console.log("父页面收到错误:", data);
+
+			// 用 async 包裹,彻底解决 ElementUI 弹框乱触发问题!
+			this.$nextTick(async () => {
+				try {
+					await this.$confirm('导入数据存在问题,是否下载错误详情文件?', '提示', {
+						confirmButtonText: '是',
+						cancelButtonText: '否',
+						type: 'warning',
+						distinguishCancelAndClose: true // 关键:区分取消和关闭
+					});
+
+					// ========== 点 【是】 才会走这里 ==========
+					console.log("用户确认下载,文件名:", data.excelFileName);
+
+					this.financeInvoiceService.downloadImportResult(data.excelFileName).then(res => {
+						this.$utils.downloadExcel(res, '发票导入错误结果.xlsx');
+						this.$message.success('文件下载成功!');
+					}).catch(err => {
+						this.$message.error('下载失败');
+					});
+
+				} catch (err) {
+					// ========== 点 【否】 或 关闭 走这里 ==========
+					this.$message.info('已取消下载');
+				}
+			})
+		},
+
 		showHide() {
 			if (this.showHideItem === false) {
 				this.showHideItem = true

+ 154 - 0
src/views/finance/invoice/InvoiceListImport.vue

@@ -0,0 +1,154 @@
+<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
+	<div>
+		<el-dialog :title="title" :close-on-click-modal="false" draggable width="1000px" height="500px" @close="close"
+			append-to-body v-model="visible">
+			<el-form :model="inputForm" ref="inputForm" :class="method === 'view' ? 'readonly' : ''"
+				:disabled="status === 'audit' || status === 'taskFormDetail'" label-width="135px"
+				@submit.native.prevent>
+				<el-row :gutter="0">
+				</el-row>
+				<el-row :gutter="0">
+					<el-col :span="24">
+						<el-form-item label="附件上传:">
+							<el-upload ref="upload" :on-remove="handleRemove" action="" :limit="1" :auto-upload="false"
+								:on-change="beforeUploadDetail" :show-file-list="true">
+								<el-button type="primary">点击上传Excel文件</el-button>
+							</el-upload>
+						</el-form-item>
+					</el-col>
+				</el-row>
+
+			</el-form>
+
+
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button type="warning" @click="downloadTpl()" plain>下载模板</el-button>
+					<el-button @click="close()" icon="el-icon-circle-close">关闭</el-button>
+					<el-button v-if="method !== 'view'" type="primary" icon="el-icon-circle-check"
+						@click="doSubmit()">确定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+
+import FinanceInvoiceService from '@/api/finance/invoice/FinanceInvoiceService'
+const financeInvoiceService = new FinanceInvoiceService()
+export default {
+	data() {
+		return {
+			visible: false,
+			title: '',
+			inputForm: {
+				file: []
+			}
+		}
+	},
+	created() {
+	},
+	components: {
+	},
+	methods: {
+		handleRemove() {
+			this.inputForm.file.pop()
+			console.log(this.inputForm.file);
+		},
+		init() {
+			this.visible = true
+			this.title = '收款发票导入'
+			this.inputForm = {
+				file: []
+			}
+			this.$forceUpdate()
+		},
+
+		beforeUploadDetail(file) {
+			this.inputForm.file.push(file)
+		},
+
+		// 下载模板
+		downloadTpl() {
+			const loading = this.$loading({
+				lock: true,
+				text: '模板下载中,请稍后',
+				spinner: 'el-icon-loading',
+				background: 'rgba(255, 255, 255, 0.3)'
+			});
+			financeInvoiceService.exportInvoiceTemplate().then((res) => {
+				// 将二进制流文件写入excel表,以下为重要步骤
+				this.$utils.downloadExcel(res, '收款发票批量导入模板' + ".xlsx")
+				loading.close();
+			}).catch(function (err) {
+				loading.close();
+				if (err.response) {
+					console.log(err.response)
+				}
+			})
+		},
+		doSubmit() {
+			if (this.inputForm.file.length == 0) {
+				this.$message.warning('未上传文件,无法提交导入')
+				return
+			}
+			const loading = this.$loading({
+				lock: true,
+				text: '导入中,请稍后',
+				spinner: 'el-icon-loading',
+				background: 'rgba(255, 255, 255, 0.3)'
+			});
+			const formBody = new FormData()
+			formBody.append('file', this.inputForm.file[0].raw)
+
+			financeInvoiceService.importProjectRecords(formBody).then((data) => {
+				loading.close();
+
+				console.log("后端返回数据:", data); // 调试看有没有返回
+				console.log("excelFileName:", data?.excelFileName);
+
+				// ==========================================
+				// 关键:只要有文件名,就强制触发父页面事件
+				// ==========================================
+				if (data && data.excelFileName) {
+					this.$emit('import-error', data);
+					this.$message.warning("导入存在异常,请下载查看");
+					this.close(); // ✅ 加这一行,子弹窗自动关闭,父页面弹框更清晰
+					return;
+				}
+
+				// 正常成功
+				this.$message.success('导入成功');
+				this.close();
+				this.$emit("refreshDataList");
+
+			}).catch(err => {
+				loading.close();
+				this.$message.error('导入失败:服务异常');
+			})
+		},
+		close() {
+			this.visible = false
+			this.$refs.inputForm.resetFields()
+			this.$refs.upload.clearFiles();
+			this.inputForm = {
+				file: []
+			}
+		}
+
+	}
+}
+</script>
+
+<style scoped>
+/deep/ .el-input-number .el-input__inner {
+	text-align: left;
+}
+
+.el-row {
+	display: flex;
+	align-items: center;
+	/* 垂直居中 */
+}
+</style>