发票开出
parent
d0db0838cd
commit
8796baeec1
@ -0,0 +1,473 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-modal width="1600px" @cancel="open = false" :visible="open" title="添加发票申请明细" :footer="null">
|
||||
<div style="padding-bottom:5px ;">
|
||||
<div>
|
||||
<BasicTable class="ds-table" @row-click="handleClick" @register="registerTable" >
|
||||
<template #right>
|
||||
<div style="width: 40%;margin-left: 10px;margin-top:10px;">
|
||||
<div>
|
||||
<BasicTable class="ds-table" @register="registerTable1">
|
||||
<template #tableTitle>
|
||||
<div>
|
||||
<span class="bold">发票申请明细</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'applyAmount'">
|
||||
<a-input-number @change="onSelectAmount" :precision="2" size="small"
|
||||
:controls="false" :max="record.restAmount"
|
||||
v-model:value="record.applyAmount" />
|
||||
</template>
|
||||
<template v-if="column.dataIndex == 'feeType'">
|
||||
<span v-if="record.feeType == 1">应收</span>
|
||||
<span v-if="record.feeType == 2">应付</span>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #tableTitle>
|
||||
<div>
|
||||
<div>
|
||||
<span class="bold">发票申请</span>
|
||||
<a-button v-repeat type="link" @click="addDetailed()">
|
||||
<span class="iconfont icon-jia"></span>
|
||||
添加申请明细
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
<template v-slot:bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'businessType'">
|
||||
<span v-if="record.businessType == 1">海运出口</span>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
<a-modal width="400px" @cancel="exchangeFlag = false" @ok="handleSureExhange" :visible="exchangeFlag"
|
||||
title="币别汇率折算">
|
||||
<div v-for="(item, index) in exchangarr" :key="index">
|
||||
<div style="margin-bottom: 15px;margin-top: 10px">
|
||||
<span class="bold" style="margin-right: 10px;width:46px;display: inline-block">1{{ item.currencyTo
|
||||
}}
|
||||
=</span>
|
||||
<a-input-number addon-after="RMB" :precision="4" size="small" :controls="false"
|
||||
v-model:value="item.reverseRate" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a-modal>
|
||||
<a-modal width="1600px" @cancel="freeFlag = false" :visible="freeFlag" title="添加发票申请明细" :footer="null">
|
||||
<div style="padding-bottom:5px ;">
|
||||
<div>
|
||||
<BasicTable class="ds-table" @row-click="handleClickFree" @register="registerTableFree">
|
||||
<template #right>
|
||||
<div style="width: 40%;margin-left: 10px;margin-top:10px;">
|
||||
<div>
|
||||
<BasicTable class="ds-table" @register="registerTable1">
|
||||
<template #tableTitle>
|
||||
<div>
|
||||
<span class="bold">发票申请明细</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'applyAmount'">
|
||||
<a-input-number @change="onSelectAmount" :precision="2" size="small"
|
||||
:controls="false" :max="record.restAmount"
|
||||
v-model:value="record.applyAmount" />
|
||||
</template>
|
||||
<template v-if="column.dataIndex == 'feeType'">
|
||||
<span v-if="record.feeType == 1">应收</span>
|
||||
<span v-if="record.feeType == 2">应付</span>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #tableTitle>
|
||||
<div>
|
||||
<div>
|
||||
<span class="bold">发票申请</span>
|
||||
<a-button v-repeat type="link" @click="addDetailed()">
|
||||
<span class="iconfont icon-jia"></span>
|
||||
添加申请明细
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
<template v-slot:bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'businessType'">
|
||||
<span v-if="record.businessType == 1">海运出口</span>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { formatParams } from '/@/hooks/web/common'
|
||||
import { ref, defineExpose, watch } from 'vue'
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table'
|
||||
import { applyColums, applySearch, applyColumsDetail,freeSearch,freeColums } from '../columns'
|
||||
import { GetApplicationList, GetApplicationDetails, GetExchangeRate,FreeInvoiceGetBizList,FreeInvoiceGetFees } from '../api.js'
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
const { createMessage } = useMessage()
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const open = ref(false)
|
||||
|
||||
const dataSource = ref([]) as any
|
||||
const [registerTable, { getForm, setSelectedRowKeys, getSelectRows, setProps, getRawDataSource, setLoading }] = useTable({
|
||||
api: async (p) => {
|
||||
const res: API.DataResult = await GetApplicationList(p)
|
||||
return new Promise((resolve) => {
|
||||
resolve({ data: [...res.data], total: res.count })
|
||||
})
|
||||
},
|
||||
beforeFetch: (p) => {
|
||||
return formatParams(p)
|
||||
},
|
||||
columns: applyColums,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: applySearch,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showIndexColumn: false,
|
||||
maxHeight: '600',
|
||||
pagination: true,
|
||||
rowSelection: {},
|
||||
striped: true,
|
||||
bordered: true,
|
||||
indexColumnProps: {
|
||||
width: 60,
|
||||
},
|
||||
canResize: true,
|
||||
immediate: false,
|
||||
})
|
||||
const [registerTable1, { getSelectRows: getSelectRowsFee, setProps: setPropsFee, setTableData }] = useTable({
|
||||
columns: applyColumsDetail,
|
||||
useSearchForm: false,
|
||||
showIndexColumn: false,
|
||||
maxHeight: '600',
|
||||
pagination: false,
|
||||
dataSource: dataSource.value,
|
||||
striped: true,
|
||||
rowKey: 'recordId',
|
||||
bordered: true,
|
||||
indexColumnProps: {
|
||||
width: 60,
|
||||
},
|
||||
canResize: true,
|
||||
immediate: false,
|
||||
})
|
||||
const [registerTableFree, { getForm:getFormFree, setSelectedRowKeys:setSelectedRowKeysFree, getSelectRows:getSelectRowsFree }] = useTable({
|
||||
api: async (p) => {
|
||||
const res: API.DataResult = await FreeInvoiceGetBizList(p)
|
||||
return new Promise((resolve) => {
|
||||
resolve({ data: [...res.data], total: res.count })
|
||||
})
|
||||
},
|
||||
beforeFetch: (p) => {
|
||||
return formatParams(p)
|
||||
},
|
||||
columns: freeColums,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: freeSearch,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showIndexColumn: false,
|
||||
maxHeight: '600',
|
||||
pagination: true,
|
||||
rowSelection: {},
|
||||
striped: true,
|
||||
bordered: true,
|
||||
indexColumnProps: {
|
||||
width: 60,
|
||||
},
|
||||
canResize: true,
|
||||
immediate: false,
|
||||
})
|
||||
const exchangeFlag = ref(false)
|
||||
const freeFlag = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const emits = defineEmits(['updateList', 'addLeft'])
|
||||
// 确定汇率 转换外币成RMB
|
||||
function handleSureExhange() {
|
||||
applications.value.forEach(item => {
|
||||
if (item.currency != 'RMB') {
|
||||
exchangarr.value.forEach(ite => {
|
||||
if (item.currency == ite.currencyTo) {
|
||||
item.exchangeRate = ite.reverseRate
|
||||
}
|
||||
})
|
||||
} else {
|
||||
item.exchangeRate = 1
|
||||
}
|
||||
})
|
||||
const resultMap = new Map();
|
||||
applications.value.forEach(item => {
|
||||
const { applicationId, currency, exchangeRate } = item;
|
||||
if (!resultMap.has(applicationId)) {
|
||||
resultMap.set(applicationId, {
|
||||
applicationId,
|
||||
exchangeRates: []
|
||||
});
|
||||
}
|
||||
const entry = resultMap.get(applicationId);
|
||||
entry.exchangeRates.push({
|
||||
currency,
|
||||
exchangeRate
|
||||
});
|
||||
});
|
||||
const result = Array.from(resultMap.values());
|
||||
emits('updateList', result)
|
||||
open.value = false
|
||||
exchangeFlag.value = false
|
||||
}
|
||||
function handleClick(record, index) {
|
||||
setSelectedRowKeys([record.id])
|
||||
const data = {
|
||||
ids: [record.id],
|
||||
businessType: record.businessType,
|
||||
}
|
||||
loading.value = true
|
||||
GetApplicationDetails(data).then(res => {
|
||||
if (res.succeeded) {
|
||||
dataSource.value = res.data
|
||||
dataSource.value.forEach(item => {
|
||||
item.applyAmount = item.restAmount ? item.restAmount : 0
|
||||
item.applyAmount = item.applyAmount.toFixed(2)
|
||||
})
|
||||
setTableData(dataSource.value)
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
function handleClickFree(record, index) {
|
||||
setSelectedRowKeysFree([record.id])
|
||||
const data = {
|
||||
id: record.id,
|
||||
businessType: record.businessType,
|
||||
// customerId: record.customerId
|
||||
}
|
||||
loading.value = true
|
||||
FreeInvoiceGetFees(data).then(res => {
|
||||
if (res.succeeded) {
|
||||
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
function init(data) {
|
||||
if(route.query.type=='apply'){
|
||||
open.value = true
|
||||
}
|
||||
if(route.query.type=='free'){
|
||||
freeFlag.value = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const amountArr = ref([]) as any
|
||||
// 计算总共的申请金额
|
||||
function onSelectAmount() {
|
||||
const arr = getSelectRowsFee()
|
||||
amountArr.value = []
|
||||
arr.forEach(item => {
|
||||
amountArr.value.push({
|
||||
currency: item.currency,
|
||||
applyAmount: item.applyAmount ? Number(item.applyAmount).toFixed(2) : 0
|
||||
})
|
||||
})
|
||||
amountArr.value = mergeByCurrency(amountArr.value)
|
||||
}
|
||||
// 合并币别
|
||||
function mergeByCurrency(arr) {
|
||||
const result = {};
|
||||
arr.forEach(item => {
|
||||
if (result[item.currency]) {
|
||||
result[item.currency].applyAmount += item.applyAmount;
|
||||
} else {
|
||||
result[item.currency] = { currency: item.currency, applyAmount: item.applyAmount };
|
||||
}
|
||||
});
|
||||
return Object.values(result);
|
||||
}
|
||||
const applications = ref([]) as any
|
||||
const exchangarr = ref([]) as any
|
||||
// 添加发票明细
|
||||
function addDetailed() {
|
||||
const arrRight = getSelectRows() ? getSelectRows() : []
|
||||
const arr1 = []
|
||||
exchangarr.value = []
|
||||
applications.value = []
|
||||
const firstCustomerId = arrRight[0].customerId;
|
||||
if (arrRight.length == 0) {
|
||||
createMessage.error('请选择一条数据')
|
||||
return false
|
||||
}
|
||||
for (let i = 1; i < arrRight.length; i++) {
|
||||
if (arrRight[i].customerId !== firstCustomerId) {
|
||||
createMessage.error('请选择同一客户')
|
||||
throw new Error('customerId值不一致');
|
||||
}
|
||||
}
|
||||
arrRight.forEach(item => {
|
||||
if (item.customerId != customerIdP.value) {
|
||||
arr1.push(1)
|
||||
}
|
||||
if (item.currency) {
|
||||
const arr = item.currency.split(' ')
|
||||
arr.forEach(ite => {
|
||||
applications.value.push({
|
||||
applicationId: item.id,
|
||||
currency: ite,
|
||||
exchangeRate: 0
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
const currencySet = new Set();
|
||||
const arr = [] as any
|
||||
applications.value.forEach(item => {
|
||||
if (item.currency != 'RMB') {
|
||||
arr.push(item)
|
||||
}
|
||||
})
|
||||
const uniqueData = arr.filter(item => {
|
||||
if (!currencySet.has(item.currency)) {
|
||||
currencySet.add(item.currency);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const promises = uniqueData.map(item => {
|
||||
return new Promise((resolve) => {
|
||||
const data = {
|
||||
currencyFrom: 'RMB',
|
||||
currencyTo: item.currency,
|
||||
}
|
||||
GetExchangeRate(data).then(res => {
|
||||
exchangarr.value.push(res.data)
|
||||
resolve(true);
|
||||
})
|
||||
});
|
||||
});
|
||||
return Promise.all(promises).then(() => {
|
||||
applications.value.forEach(item => {
|
||||
exchangarr.value.forEach(ite => {
|
||||
if (item.currency == ite.currencyTo) {
|
||||
item.exchangeRate = ite.reverseRate
|
||||
}
|
||||
})
|
||||
})
|
||||
exchangeFlag.value = true
|
||||
});
|
||||
}
|
||||
|
||||
const customerIdP = ref('')
|
||||
function changeCustIn(id) {
|
||||
customerIdP.value = id
|
||||
}
|
||||
|
||||
defineExpose({ init, changeCustIn })
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.total {
|
||||
padding-left: 10px;
|
||||
text-align: right;
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid rgba(230, 236, 241, 1);
|
||||
padding-top: 5px;
|
||||
|
||||
.total-item {
|
||||
background: rgba(245, 249, 252, 1);
|
||||
padding: 3px 10px;
|
||||
font-size: 12px;
|
||||
margin-right: 10px;
|
||||
|
||||
.number {
|
||||
color: rgba(37, 122, 250, 1);
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .ant-table-footer {
|
||||
padding: 0px !important;
|
||||
background: white;
|
||||
}
|
||||
|
||||
/deep/ .ant-table-container {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.apply {
|
||||
background-color: #f5f9fc;
|
||||
cursor: pointer;
|
||||
padding: 4px 10px;
|
||||
margin-right: 20px;
|
||||
border: 1px solid white;
|
||||
|
||||
/deep/ .ant-checkbox-wrapper {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border: 1px solid #5396fa;
|
||||
}
|
||||
}
|
||||
|
||||
.rmb {
|
||||
color: #ba3849;
|
||||
font-weight: bold;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.yb {
|
||||
color: #17a6a3;
|
||||
font-weight: bold;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
/deep/ .ant-checkbox-wrapper-checked .ant-checkbox-inner {
|
||||
background-color: #257afa;
|
||||
border-color: #257afa;
|
||||
}
|
||||
|
||||
/deep/ .ant-checkbox-disabled.ant-checkbox-checked .ant-checkbox-inner::after {
|
||||
border-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/deep/ .ant-checkbox-wrapper-checked .ant-checkbox-disabled+span {
|
||||
color: #257afa;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-modal width="800px" @cancel="openFileFlag = false" :visible="openFileFlag" title="文件列表" :footer="null">
|
||||
<div style="padding-bottom: 20px">
|
||||
<div style="display: flex;">
|
||||
<DsFile @handleSuccess="fileSuccess" ref="dsFile" :showFileList="false" :id="route.query.id"
|
||||
:show="false" height="300">
|
||||
<a-button type="link" @click="addFile">
|
||||
<span class="iconfont icon-jia"></span>
|
||||
附件上传
|
||||
</a-button>
|
||||
</DsFile>
|
||||
<a-button type="link" @click="delFile">
|
||||
<span class="iconfont icon-shanchu1"></span>
|
||||
删除文件
|
||||
</a-button>
|
||||
</div>
|
||||
<a-table :loading="fileloading" size="small" :pagination="false" rowKey="id"
|
||||
:row-selection="{ selectedRowKeys: selectedRowKeysFile, onChange: onSelectChangeFile }"
|
||||
:columns="columnsFile" :data-source="fileData" bordered>
|
||||
<template v-slot:bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'fileName'">
|
||||
<a @click="download(record)">{{ record.fileName }}</a>
|
||||
</template>
|
||||
<template v-if="column.dataIndex == 'caozuo'">
|
||||
<a @click="lookFile(record)">预览</a>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</a-modal>
|
||||
<a-modal width="800px" @cancel="fileFlag = false" :visible="fileFlag" title="预览文件" :footer="null">
|
||||
<img v-if="!pdfFlag" :src="fileSrc" alt="">
|
||||
<iframe v-if="pdfFlag" :src="fileSrc" style="height: 800px;width: 100%;" frameborder="0"></iframe>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup >
|
||||
import {
|
||||
GetOpFileList, BatchDelFiles, DownloadOpFile
|
||||
} from '../api'
|
||||
import DsFile from '/@/components/File/index.vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ref, onMounted, defineComponent, nextTick, watchEffect, watch } from 'vue'
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
const { createMessage } = useMessage()
|
||||
const route = useRoute()
|
||||
const columnsFile = [
|
||||
{
|
||||
title: '文件名称',
|
||||
width: 150,
|
||||
dataIndex: 'fileName',
|
||||
},
|
||||
{
|
||||
title: '上传日期',
|
||||
width: 150,
|
||||
dataIndex: 'createTime',
|
||||
},
|
||||
{
|
||||
title: '上传者',
|
||||
width: 150,
|
||||
dataIndex: 'createUserName',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'caozuo',
|
||||
},
|
||||
]
|
||||
const openFileFlag = ref(false)
|
||||
const dsFile = ref('')
|
||||
function download(item) {
|
||||
DownloadOpFile({ id: item.id }).then(res => {
|
||||
const pdfUrl = window.URL.createObjectURL(new Blob([res.data], { type: 'application/txt;charset=utf-8' }))
|
||||
const fname = item.fileName // 下载文件的名字
|
||||
const link = document.createElement('a')
|
||||
link.href = pdfUrl
|
||||
link.setAttribute('download', fname)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
})
|
||||
}
|
||||
const pdfFlag = ref(false)
|
||||
function lookFile(item) {
|
||||
fileFlag.value = true
|
||||
if (item.fileType == '.pdf') {
|
||||
pdfFlag.value = true
|
||||
} else {
|
||||
pdfFlag.value = false
|
||||
}
|
||||
DownloadOpFile({ id: item.id }).then(res => {
|
||||
if (res.data.size > 150) {
|
||||
fileSrc.value = window.URL.createObjectURL(new Blob([res.data], { type: 'application/pdf;charset=utf-8' }))
|
||||
}
|
||||
})
|
||||
}
|
||||
function fileSuccess() {
|
||||
getfileList()
|
||||
}
|
||||
function addFile() {
|
||||
dsFile.value.init()
|
||||
}
|
||||
function init() {
|
||||
getfileList()
|
||||
openFileFlag.value = true
|
||||
}
|
||||
const fileSrc = ref('')
|
||||
const fileFlag = ref(false)
|
||||
const fileData = ref([]) as any
|
||||
const fileloading = ref(false)
|
||||
const getfileList = () => {
|
||||
fileloading.value = true
|
||||
GetOpFileList({ id: route.query.id }).then(res => {
|
||||
fileloading.value = false
|
||||
res.data.forEach(item => {
|
||||
if (item.createTime) {
|
||||
item.createTime = new Date(item.createTime)
|
||||
item.createTime = item.createTime.getFullYear() + '-' + (item.createTime.getMonth() + 1) + '-' + (item.createTime.getDate())
|
||||
}
|
||||
item['type'] = item?.filePath?.split('.')[1]
|
||||
fileData.value = res.data
|
||||
})
|
||||
if (res.data.length == 0) {
|
||||
fileData.value = []
|
||||
}
|
||||
}).catch(() => {
|
||||
fileloading.value = false
|
||||
})
|
||||
}
|
||||
const selectedRowKeysFile = ref([]) as any
|
||||
function onSelectChangeFile(selectedRowKeys) {
|
||||
selectedRowKeysFile.value = selectedRowKeys
|
||||
}
|
||||
function delFile() {
|
||||
fileloading.value = true
|
||||
BatchDelFiles({ ids: selectedRowKeysFile.value }).then(res => {
|
||||
if (res.succeeded) {
|
||||
createMessage.success('删除成功')
|
||||
getfileList()
|
||||
}
|
||||
fileloading.value = false
|
||||
})
|
||||
}
|
||||
defineExpose({init})
|
||||
</script>
|
Loading…
Reference in New Issue