emis-frontend/src/views/emis/emisTmsCostFee/BatchImportCostFee.vue
2025-11-18 16:36:17 +08:00

760 lines
26 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>
<XdPageContainer class="app-container">
<div slot="pageHeader">
<div slot="toolbar">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button plain class="filter-item" size="mini" type="primary" icon="el-icon-upload2" @click="handleBatchImport">选择文件</el-button>
<el-link type="primary" style="line-height: 28px;font-size:13px;color:blue" href="/static/tpl/import_supplier_fee_tpl.xls">下载导入Excel模板</el-link>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="handleRealUploadAll"
>批量上传</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
>删除</el-button>
</el-col>
</el-row>
</div>
</div>
<el-table
slot="pageContent"
slot-scope="slotProps"
:height="(slotProps.contentHeight)+'px'"
storageName="BatchImportCostFee_main1"
ref="multipleTable"
:row-class-name="tableRowClassName"
v-loading="loading"
border
size="mini"
:data="orderList"
:showSearch.sync="showSearch"
:queryParams="queryParams"
:total="total"
@queryTable="getList"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
show-summary
:summary-method="getSummaries"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column :label="$t('状态')" align="center" prop="uploadStatus" min-width="80" :show-overflow-tooltip="true" >
<template slot-scope="scope">
<el-tag v-if="scope.row.uploadStatus==1" type="success">成功</el-tag>
<el-tag v-if="scope.row.uploadStatus==0" type="info">待处理</el-tag>
<el-tag v-if="scope.row.uploadStatus==-1" type="danger">异常</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('异常信息')" align="center" prop="errorInfo" min-width="300" :show-overflow-tooltip="true" >
<template slot-scope="scope">
<span v-if="scope.row.uploadStatus==-1" style="color:red">{{ scope.row.errorInfo }}</span>
<span v-if="scope.row.uploadStatus==1">上传成功</span>
<span v-if="scope.row.uploadStatus==0">待上传</span>
</template>
</el-table-column>
<el-table-column
v-for="item in dynamicCol"
:key="item.prop"
align="center"
:prop="item.prop"
min-width="100"
:show-overflow-tooltip="true"
:label="item.label"
/>
<!-- <el-table-column v-for="(item, index) in dynamicCol" :key="index" :label="item.label" :prop="item.prop">
</el-table-column> -->
<!-- <el-table-column :label="$t('线路名称')" align="center" prop="lineName" min-width="120" />
<el-table-column :label="$t('产品名称')" align="center" prop="productTypeName" min-width="200" />
<el-table-column :label="$t('起始网点')" align="center" prop="startSiteName" min-width="100" />
<el-table-column :label="$t('下一网点')" align="center" prop="nextSiteName" min-width="100" />
<el-table-column :label="$t('节点负责人')" align="center" prop="manager" min-width="100" />
<el-table-column :label="$t('供应商')" align="center" prop="supplierName" min-width="180" />
<el-table-column :label="$t('生效时间')" align="center" prop="startDate" min-width="100" />
<el-table-column :label="$t('失效时间')" align="center" prop="endDate" min-width="100" /> -->
</el-table>
<el-dialog title="上传文件" :visible.sync="openUploadForm" width="50%" v-dialogDrag :destroy-on-close="true" :close-on-click-modal="false" append-to-body>
<el-row :gutter="0">
<el-form ref="form" :model="form" :rules="rules" size="mini" label-width="80px">
<el-col :span="24">
<el-form-item label="选择文件" prop="uploadFile" >
<el-upload
action=""
name="file"
accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
:show-file-list="true"
:limit="1"
:file-list="fileList"
:tooltip="fileName"
:on-change="fileChange"
:before-upload="beforeUpload">
<el-button slot="trigger" plain class="filter-item" size="mini" type="primary" >选择文件</el-button>
<el-link type="primary" style="line-height: 28px;font-size:13px;color:blue" href="/static/tpl/import_supplier_fee_tpl.xls">下载导入Excel模板</el-link>
<div v-if="uploadExcelDataInfo !=''" style="color: black;font-size: 12px;font-weight: 800;">{{ fileName }} {{ uploadExcelDataInfo }}</div>
</el-upload>
<el-alert
title="注意"
type="info"
description="仅允许导入“xls”或“xlsx”格式文件!"
close-text="知道了">
</el-alert>
</el-form-item>
</el-col>
</el-form>
</el-row>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="batchImpDataForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</XdPageContainer>
</template>
<script>
import { draftSubmitTmsCostFee } from "@/api/emis/emisTmsCostFee";
import { listEmisTmsCostFee, getEmisTmsCostFee, delEmisTmsCostFee, addEmisTmsCostFee, updateEmisTmsCostFee } from "@/api/emis/emisTmsCostFee";
import {listEmisSettleSubBill} from "@/api/emis/emisSettleSubBill";
import { listEmisTmsSiteBatchDtl} from "@/api/emis/emisTmsSiteBatchDtl";
import { listEmisTmsSiteBatch,listBySupplerAndCarNo} from "@/api/emis/emisTmsSiteBatch";
import { getSupplierByName } from "@/api/emis/emisSupplier";
import {timeDiffTime,timeFormat} from '@/utils/dateUtils'
import { formatSearchStr,deepClone } from '@/utils/index'
import XLSX from 'xlsx'
import {parseExcelTime } from "@/utils/custom";
import { forEach } from "lodash";
export default {
name: "BatchImportCostFee",
components: {
},
dicts: ['emis_cost_fee_type'],
data() {
return {
resetRefreshId:null,
// 遮罩层
loading: false,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 运单列表表格数据
orderList: [],
tmpDataList: [],
selectionList:[],
selectionIndexList: [],
feeItemMap:new Map(),
feeItemCurrencyMap:new Map(),
// 动态费用列表
dynamicCol:[],
form:{},
rules:{},
openUploadForm:false,
uploadMsg:"",
uploadExcelDataInfo:"",
fileName:"",
fileList:[],
// 查询参数
queryParams: {
lineCode: null,
productType: null,
startSiteCode: null,
nextSiteCode: null,
manager: null,
supplierCode: null,
startDate: null,
endDate: null,
remark: null,
},
};
},
created() {
if(this.$store.getters.ownerSiteCode!='88888'){
this.queryParams.sendSiteCode=this.$store.getters.ownerSiteCode;
}
},
mounted() {
},
methods: {
getUUID() {
return Math.random().toString(36).substring(3,10);
},
handleBatchImport(){
this.resetRefreshId="importForm-"+this.getUUID();
this.openUploadForm=true;
this.fileList=[];
this.uploadExcelDataInfo="";
this.fileName="";
},
calcDeliveryTime(startData,endDate){
return timeDiffTime(startData,endDate)
},
//标红table指定行
tableRowClassName(row, index) {
row.row.index = row.rowIndex;
if (row.uploadStatus=='1') {
return 'success-row';
}
else if (row.uploadStatus=='-1') {
return 'warning-row';
}
return '';
},
/** 查询运单列表列表 */
getList() {
this.loading = true;
},
fileChange(file, fileList) {
this.fileList = fileList;
},
beforeUpload(file) {
this.fileName = file.name;
this.form.batchName = file.name;
this.readerData(file)
return false
},
readerData(rawFile) {
const that=this;
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
const workbook = XLSX.read(data, { type: 'array' })
const firstSheetName = workbook.SheetNames[0] // firstSheetName
// if (!excelTypes.includes(firstSheetName)) {
// reject('无法识别当前Excel模板')
// this.$modal.msgError('无法识别当前Excel模板')
// return
// }
const worksheet = workbook.Sheets[firstSheetName]
// const header = this.getHeaderRow(worksheet) // 标题
const results = XLSX.utils.sheet_to_json(worksheet)
that.dynamicCol = [];
const keysSet = new Set(); // 用Set存储key,自动去重
// 遍历所有results中的对象
for (const item of results) {
// 遍历当前对象的每个key
for (const key in item) {
// 添加到Set(重复的key会被自动忽略)
keysSet.add(key);
}
}
// 将Set转换为数组,并映射为目标格式
that.dynamicCol = Array.from(keysSet).map(key => ({
label: key,
prop: key
}));
// 转换费用类型
that.feeItemMap=new Map();;
for(let i=0;i<that.dict.type.emis_cost_fee_type.length;i++){
let item=that.dict.type.emis_cost_fee_type[i];
that.feeItemMap[""+item.label+""]=item.value;
}
that.batchImportData(results);
resolve()
}
reader.readAsArrayBuffer(rawFile)
})
},
getHeaderRow(sheet) {
const headers = []
const range = XLSX.utils.decode_range(sheet['!ref'])
let C
const R = range.s.r
/* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
/* find the cell in the first row */
let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
headers.push(hdr)
}
return headers
},
async batchImportData(results) {
const that=this;
const _index = 1
let loadingInstance = this.$loading({
text: '正在解析 ' + _index + ' 数据...'
})
this.orderList = [];
let lastOrderSn = '';
let orderIndex = 0;
// 组装提交的数据
for (let index = 0; index < results.length; index++) {
const element = results[index]
// tst
let rowIndex=element['__rowNum__']
let orderData={
uploadStatus: 0,
errorInfo:'',
}
let feeItemList=[];
let feeItemPrice=new Map();
let feeItemCurrencyMap=new Map();
let errorInfo='';
let batchNo=null;
// 费用处理
for (const key in element) {
if (element.hasOwnProperty(key)) {
this.$set(orderData,key,element[key]) ;
// 解析费用数据
if(key!='运输单号'&&key!='供应商名称'&&key!='账期'&&key!='备注'&&key!='成本时间'){
// 获取费用编码
// 单价和币种特殊处理
let indexPos=key.indexOf("单价");
let indexPosCurrency = key.indexOf("币种");
if(indexPos !== -1 ){
let feeTypeStr=key.substring(0,indexPos);
if(!(feeTypeStr in that.feeItemMap) ){
orderData.uploadStatus=-1;
errorInfo=errorInfo+''+key+',';
continue
}else{
let feeTypeCode=that.feeItemMap[feeTypeStr]
feeItemPrice[feeTypeCode]=element[key];
}
}
else if(indexPosCurrency !== -1 ){
let feeTypeStr=key.substring(0,indexPosCurrency);
if(!(feeTypeStr in that.feeItemMap) ){
orderData.uploadStatus=-1;
errorInfo=errorInfo+''+key+',';
continue
}else{
let feeTypeCode=that.feeItemMap[feeTypeStr]
feeItemCurrencyMap[feeTypeCode]=element[key];
}
}
else{
if(!(key in that.feeItemMap) ){
orderData.uploadStatus=-1;
errorInfo=errorInfo+''+key+',';
continue
}
let feeItem={
feeType:that.feeItemMap[key],
price:null,
fee:element[key],
currency:'CNY'
}
feeItemList.push(feeItem);
}
}
}
}
if(orderData.uploadStatus==-1){
orderData.errorInfo="费用类型:["+errorInfo+"]未配置";
}
else{
let costFee=Number(0);
let usdCostFee=Number(0);
// 循环处理费用单价和币种
for(let i=0;i<feeItemList.length;i++){
let item=feeItemList[i];
let price=feeItemPrice[item.feeType];
this.$set(item,"price",price) ;
let currency='CNY';
if(item.feeType in feeItemCurrencyMap){
currency=feeItemCurrencyMap[item.feeType];
}
this.$set(item,"currency",currency) ;
if(currency == "CNY"){
costFee=costFee+Number(item.fee);
}
else if(currency == "USD"){
usdCostFee=usdCostFee+Number(item.fee);
}
}
this.$set(orderData,"costFee",costFee) ;
this.$set(orderData,"usdCostFee",usdCostFee) ;
this.$set(orderData,"feeItemList",feeItemList) ;
}
// 判断供应商是否存在
let supplierCode=null;
// 通过供应商名称找到供应商编号,必须完全匹配
let resSuppler= await getSupplierByName(orderData['供应商名称']);
if(resSuppler&&resSuppler.code==200){
let supplier = resSuppler.data;
if(supplier==null){
orderData.uploadStatus=-1;
orderData.errorInfo='供应商不存在';
}else{
supplierCode=supplier.code
this.$set(orderData,"supplierCode",supplierCode) ;
}
}else{
orderData.uploadStatus=-1;
orderData.errorInfo='供应商不存在';
}
if(orderData.uploadStatus!=-1){
// 判断批次是否存在 获取批次信息
let carNo=orderData['运输单号'];
let etd=orderData['成本时间'];
// let resBatch=await listBySupplerAndCarNo(supplierCode,carNo,etd);
let resBatch=await listBySupplerAndCarNo(supplierCode,carNo);
if(resBatch&&resBatch.code==200){
if(resBatch.rows&&resBatch.rows.length==0){
orderData.uploadStatus=-1;
orderData.errorInfo='批次不存在';
}
else if(resBatch.rows&&resBatch.rows.length>1){
orderData.uploadStatus=-1;
orderData.errorInfo='存在多个批次';
}
else{
let batchItem=resBatch.rows[0];
if(etd && batchItem.etd!==etd){
orderData.uploadStatus=-1;
orderData.errorInfo='成本时间不一致';
}else{
this.$set(orderData,"batchItem",batchItem) ;
this.$set(orderData,"batchNo",batchItem.batchNo) ;
}
}
}else{
orderData.uploadStatus=-1;
orderData.errorInfo='获取批次异常';
}
}
this.tmpDataList.push(orderData);
}
this.uploadExcelDataInfo="总共 " + this.tmpDataList.length +" 条数据。";
loadingInstance.close()
},
handleRealUploadAll(){
if (this.ids && this.ids.length>0) {
this.$confirm("确认上传吗?", "提示", {
cancelButtonText: "取消",
confirmButtonText: "确认",
type: "warning",
distinguishCancelAndClose: true,
closeOnClickModal: false
}).then(async () => {
if(this.selectionList&&this.selectionList.length>0){
let loadingInstance = this.$loading({
text: '正在批量提交...'
})
for(let i=0;i<this.selectionList.length;i++){
let orderItem=this.selectionList[i];
// 数据转化为标准提交格式
let submitOrderForm={
batchNo:null,
feeType: null,
splitType: null,
transType: null,
transSignNo: null,
supplierCode: null,
supplierName: null,
billMonth:null,
sendPlace: null,
destPlace: null,
costFee: null,
tradeDate: null,
sendPieceSum: null,
pieceNumber: null,
totalWeight: null,
totalVolume: null,
blConfirmCenter: null,
realPieceNumber: null,
realTotalWeight: null,
realTotalVolume: null,
confirmCenterDate: null,
confirmCenterNote: null,
confirmCenterManCode: null,
confirmCenterCode: null,
blConfirmSite: null,
confirmSiteDate: null,
confirmSiteManCode: null,
confirmSiteNote: null,
confirmSiteCode: null,
remark: null,
billRelList:null,
feeItemList:[],
};
submitOrderForm.billMonth=orderItem['账期'];
submitOrderForm.remark=orderItem['备注'];
submitOrderForm.splitType="1";
try{
let batchNo=null;
// 获取批次信息
let batchItem=orderItem['batchItem'];
if(batchItem!=null){
batchNo=batchItem.batchNo;
submitOrderForm.batchNo=batchNo;
submitOrderForm.realTotalWeight=batchItem.transWeight;
submitOrderForm.realPieceNumber=batchItem.packNum;
submitOrderForm.transType=batchItem.transType;
submitOrderForm.transSignNo=batchItem.carNo;
submitOrderForm.sendPlace=batchItem.startSiteName;
submitOrderForm.destPlace=batchItem.nextSiteName;
submitOrderForm.supplierCode=batchItem.supplierCode;
submitOrderForm.supplierName=batchItem.supplierName;
submitOrderForm.tradeDate= this.parseTime(batchItem.etd);
submitOrderForm.costName=batchItem.batchName;
// this.form.costFee=item.startSiteName;
submitOrderForm.sendPieceSum=batchItem.totalWaybillQty;
submitOrderForm.pieceNumber=batchItem.pieceNumber;
submitOrderForm.totalWeight=batchItem.totalWeight;
submitOrderForm.totalVolume=batchItem.totalVolume;
this.$set(submitOrderForm,"costFee",orderItem.costFee) ;
this.$set(submitOrderForm,"usdCostFee",orderItem.usdCostFee) ;
// 费用类型
this.$set(submitOrderForm,"feeItemList",orderItem.feeItemList) ;
// 获取批次关联运单号
let billRelList=[];
let resBill=await listEmisTmsSiteBatchDtl({batchNo: batchNo});
if(resBill.code==200){
if(resBill.rows&&resBill.rows.length>0){
resBill.rows.forEach(itemWaybill=>{
let relItem={
billCode:itemWaybill.billCode
};
billRelList.push(relItem);
});
}
}
this.$set(submitOrderForm,"billRelList",billRelList)
let res=await draftSubmitTmsCostFee(submitOrderForm)
let rstData=res.data;
if(rstData.result=='success'){
orderItem.uploadStatus=1;
orderItem.errorInfo="上传成功";
}
else{
orderItem.uploadStatus=-1;
orderItem.errorInfo=rstData.msg;
}
}else{
// orderItem.uploadStatus=-1;
// orderItem.errorInfo='批次不存在';
continue;
}
}catch(e){
orderItem.uploadStatus=-1;
orderItem.errorInfo=''+e.message;
continue;
}
}
loadingInstance.close();
this.$modal.msgSuccess("已处理");
}
});
}
},
batchImpDataForm() {
// 判断是否有数据
if(this.tmpDataList.length==0){
this.$modal.msgError('表格中没有数据');
return;
}
this.orderList = this.orderList.concat(this.tmpDataList);
this.tmpDataList=[];
this.openUploadForm = false;
},
/** 搜索按钮操作 */
handleQuery() {
// this.queryParams.pageNum = 1;
// this.getList();
},
/** 重置按钮操作 */
resetQuery() {
// this.resetForm("queryForm");
// this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.selectionList=selection;
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
// 排序 查询字段是表格中字段名字 动态取值排序顺序
handleSortChange(column) {
this.queryParams.orderByColumn = column.prop;
this.queryParams.isAsc = column.order;
this.getList();
},
cancel(){
this.openUploadForm = false;
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
let that=this;
this.$modal.confirm('是否确认删除').then(function() {
that.selectionList.forEach((item,index) =>{
that.orderList.forEach((v,i)=>{
if(item.index === v.index){
that.orderList.splice(i,1)
}
})
})
that.$refs.multipleTable.clearSelection();
}).then(() => {
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 列索引函数 */
getIndex($index) {
return (this.queryParams.pageNum - 1) * this.queryParams.pageSize + $index + 1
},
//指定列求和
getSummaries(param) {
const { columns, data } = param;
const sums = [];
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '合计';
return;
} else if (column.property === 'uploadStatus') {
//计数
sums[index] = data.length+ ' 条';
} else if ( column.property === 'errorInfo') {
sums[index] = '';
} else {
const values = data.map(item => Number(item[column.property]));
if (!values.every(value => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
//保留2位小数
sums[index] = sums[index].toFixed(2);
} else {
sums[index] = '';
}
}
});
return sums;
}
}
};
</script>
<style lang="scss" scoped>
:deep .el-form-item__label-wrap {
margin-left: 0 !important;
}
.el-divider{
margin-top: 0px;
background: 0 0;
border-top: 1px solid #E6EBF5;
}
.el-divider__text {
color: #0955ee;
cursor: pointer;
}
.query-label {
.el-radio{
display: block;
line-height: 23px;
white-space: normal;
margin-right: 0;
}
}
.queryForm .el-form-item {
margin-bottom: 0px !important;
}
:deep .el-table .success-row {
color: rgb(21, 174, 31) !important;
}
:deep .el-table .warning-row {
color: #f51f1f !important;
}
:deep .el-form-item__label {
text-align: left;
}
</style>