费用审核

szh-new
lijingjia 6 months ago
parent c550d114b1
commit 52a95d7ed5

@ -37,6 +37,7 @@ export function BatchDelFiles(data: PageRequest) {
export function DownloadOpFile(params) {
return request<DataResult>({
url: Api.DownloadOpFile,
responseType: 'arraybuffer',
method: 'get',
params
})

@ -18,7 +18,7 @@
<template #title>
<span>下载</span>
</template>
<span class="iconfont icon-xiazai-wenjianxiazai-13" :style="{ fontSize: '14px' }" @click="downFile(item.id)"></span>
<span class="iconfont icon-xiazai-wenjianxiazai-13" :style="{ fontSize: '14px' }" @click="downFile(item)"></span>
</a-tooltip>
<a-popconfirm
title="确定要删除该文件?"
@ -93,9 +93,15 @@
})
}
//
const downFile = (id) => {
DownloadOpFile({ id }).then(res => {
const downFile = (item) => {
DownloadOpFile({ id: item.id }).then(res => {
const pdfUrl = window.URL.createObjectURL(new Blob([res], { 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()
})
}
//

@ -2,6 +2,7 @@
<div ref="wrapRef" :class="getWrapperClass" class="basic-table-search-form">
<BasicForm
v-if="getBindValues.useSearchForm"
v-show="showSearchForm"
ref="formRef"
submit-on-reset
v-bind="getFormProps"

@ -148,4 +148,8 @@ export const basicProps = {
type: String as PropType<SizeType>,
default: DEFAULT_SIZE,
},
showSearchForm: {
type: Boolean,
default: true
}
}

@ -90,3 +90,58 @@
padding: 5px 15px!important;
}
}
.ant-table-thead {
th {
background-color: #f7f9fb!important;
font-weight: 600!important;
color: #262626!important;
}
}
.ant-table-tbody {
td {
font-family: Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Arial,sans-serif!important;
font-weight: 400!important;
color: #262626!important;
}
}
.ds-approve-mian-action-bar {
.ant-btn-link, .ant-checkbox-wrapper {
font-size: 12px;
}
.ant-btn-link:hover {
color: #007aff;
}
.ant-checkbox-inner {
width: 14px;
height: 14px;
}
}
// basetbale form
.vben-basic-form {
.ant-select-selector, .ant-form-item-control-input, .ant-input-affix-wrapper, .ant-form-item-control-input {
height: 28px!important;
min-height: 28px!important;
}
.ant-select-selection-search-input {
height: 26px!important;
}
.ant-select-selection-item, .ant-select-selection-placeholder {
line-height: 26px!important;
}
.ant-select-selection-placeholder {
line-height: 26px;
}
.ant-select-selection-search-input {
height: 26px;
}
.ant-picker {
height: 28px;
}
.ant-btn {
height: 30px;
}
}

@ -4,6 +4,8 @@
@namespace: vben;
@mini-height: 28px;
// font-size
@content-size: 12px;

@ -126,6 +126,10 @@ h5 {
margin-top: 2px!important;
}
.mt5 {
margin-top: 5px!important;
}
.mt10 {
margin-top: 10px!important;
}
@ -179,3 +183,33 @@ h5 {
.ds-purple-tag {
background-color: #531dab;
}
// 使2
.ds-green-status, .ds-blue-status, .ds-orange-status, .ds-red-status, .ds-purple-status, .ds-grey-status {
font-family: Arial, Helvetica, sans-serif;
font-weight: 600;
}
.ds-green-status {
color: #419638;
}
.ds-blue-status {
color: #096dd9;
}
.ds-orange-status {
color: #E1A200;
}
.ds-red-status {
color: #F5222D;
}
.ds-purple-status {
color: #531dab;
}
.ds-grey-status {
color: #8C8C8C;
}

@ -2,7 +2,8 @@
import { request } from '/@/utils/request'
import { DataResult, PageRequest } from '/@/api/model/baseModel'
enum Api {
GetList = '/feeApi/FeeAudit/GetList'
GetList = '/feeApi/FeeAudit/GetList',
AuditByBiz = '/feeApi/FeeAudit/AuditByBiz'
}
// 业务列表
@ -13,6 +14,15 @@ export function GetList(data: PageRequest) {
data
})
}
// 批量审批
export function AuditByBiz(data: PageRequest) {
return request<DataResult>({
url: Api.AuditByBiz,
method: 'post',
data
})
}
// export function getThermometryWarningCount() {
// return request<DataResult>({
// url: Api.getThermometryWarningCount,

@ -144,56 +144,67 @@ export const columns: BasicColumn[] = [
{
dataIndex: 'businessTypeText',
title: '业务类型',
align: 'left',
width: 120
},
{
dataIndex: 'arFeeStatusText',
title: '应收费用',
align: 'left',
width: 120
},
{
dataIndex: 'apFeeStatusText',
title: '应付费用',
align: 'left',
width: 120
},
{
dataIndex: 'changeOrder',
title: '更改单',
align: 'left',
width: 120
},
{
dataIndex: 'customerName',
title: '委托单位',
align: 'left',
width: 120
},
{
dataIndex: 'mblno',
title: '主提单号',
align: 'left',
width: 120
},
{
dataIndex: 'etd',
title: '开船日期',
align: 'left',
width: 120
},
{
dataIndex: 'businessStatus',
title: '业务状态',
align: 'left',
width: 120
},
{
dataIndex: 'hblno',
title: '分提单号',
align: 'left',
width: 120
},
{
dataIndex: 'isBusinessLocking',
title: '业务锁定',
align: 'left',
width: 120
},
{
dataIndex: 'isFeeLocking',
title: '费用锁定',
align: 'left',
width: 120
},
]

@ -0,0 +1,40 @@
<!--
* @Description: 审核审批 -> 费用操作栏
* @Author: lijj
* @Date: 2024-06-13 17:08:08
-->
<template>
<div class="ds-approve-mian-action-bar">
<a-button type="link">
<span class="iconfont icon-chenggong"></span>
审核通过
</a-button>
<a-button type="link">
<span class="iconfont icon-xiajiantou"></span>
配对审核
</a-button>
<a-button type="link">驳回提交</a-button>
<a-button type="link">禁开发票</a-button>
<a-button type="link">解禁发票</a-button>
<a-button type="link">批准申请</a-button>
<a-button type="link">驳回申请</a-button>
<a-button type="link">历史申请</a-button>
<a-button type="link">查看信息</a-button>
<a-button type="link">显示工作量</a-button>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, defineProps, watch, defineEmits } from 'vue'
import { AuditByBiz } from '../api'
import { useMessage } from '/@/hooks/web/useMessage'
const { createMessage } = useMessage()
onMounted(() => {
})
</script>
<style lang="less">
.ds-approve-mian-action-bar {
}
</style>

@ -0,0 +1,227 @@
<!--
* @Description: 审核审批 -> 费用表格组件
* @Author: lijj
* @Date: 2024-06-13 17:08:08
-->
<template>
<div class="ds-approve-fee-table">
<BasicTable
@register="registerTable"
:scroll="{ x: '100%', y: '162px'}"
@selection-change="selectHandle"
>
<template #toolbar>
<FeeActionBar
></FeeActionBar>
</template>
<template v-slot:bodyCell="{ column, record }">
<template v-if="column.dataIndex == 'feeStatusText'">
<span v-if="record.feeStatus != 1" class="ds-green-status"> {{ record.feeStatusText }}</span>
<span v-else class="ds-orange-status"> {{ record.feeStatusText }}</span>
</template>
</template>
</BasicTable>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, defineProps, watch, defineExpose } from 'vue'
import { BasicTable, useTable } from '/@/components/Table'
import { useMessage } from '/@/hooks/web/useMessage'
import { GetList } from '/@/components/CostEntry/api'
import FeeActionBar from './feeActionBar.vue'
import emitter from '/@/utils/Bus'
const { notification } = useMessage()
const props = defineProps({
// id
id: {
type: Array,
default: () => {
return []
}
},
//
tbType: {
type: String,
default: null
}
})
const [registerTable, { reload, setTableData, getDataSource, getSelectRows }] = useTable({
title: props.tbType == 'receive' ? '应收费用' : '应付费用',
striped: false,
api: async (p) => {
const res = await GetList(p)
return new Promise((resolve) => {
res.data.forEach(item => {
// item.stlDate = formatTableData(item.stlDate)
// item.etd = formatTableData(item.etd)
// item.closingDate = formatTableData(item.closingDate)
// item.eta = formatTableData(item.eta)
// item.issueDate = formatTableData(item.issueDate)
// item.customDate = formatTableData(item.customDate)
// item.inspectionDate = formatTableData(item.inspectionDate)
})
resolve({ data: [...res.data], total: res.count })
})
},
beforeFetch: () => {
const postParam = {
pageCondition: {
pageIndex: 1,
pageSize: 1000,
sortConditions: []
},
queryCondition: JSON.stringify([
{ FieldName: 'BusinessId', FieldValue: props.id[props.id.length - 1]?.id, ConditionalType: 1 },
{ FieldName: 'FeeType', FieldValue: props.tbType == 'receive' ? 1 : 2, ConditionalType: 1 },
{ FieldName: 'FeeStatus', FieldValue: 2, ConditionalType: 1 }
])
}
return postParam
},
afterFetch: () => {
},
columns: [
{
dataIndex: 'feeStatusText',
title: '费用状态',
align: 'left',
width: 90
},
{
dataIndex: 'feeName',
title: props.tbType == 'receive' ? '应收费用名称' : '应付费用名称',
align: 'left',
width: 130
},
{
dataIndex: 'feeEnName',
title: '费用英文名称',
align: 'left',
width: 130
},
{
dataIndex: 'customerTypeText',
align: 'left',
title: '客户类别',
width: 130
},
{
dataIndex: 'customerName',
align: 'left',
title: '结算对象',
width: 130
},
{
dataIndex: 'unitText',
align: 'left',
title: '单位标准',
width: 120
},
{
dataIndex: 'taxRate',
align: 'left',
title: '税率',
width: 120
},
{
dataIndex: 'unitPrice',
align: 'left',
title: '单价',
width: 120
},
{
dataIndex: 'quantity',
align: 'left',
title: '数量',
width: 120
},
{
dataIndex: 'noTaxPrice',
align: 'left',
title: '不含税单价',
width: 120
},
{
dataIndex: 'noTaxAmount',
align: 'left',
title: '不含税金额',
width: 120
},
{
dataIndex: 'amount',
align: 'left',
title: '金额',
width: 120
},
{
dataIndex: 'currency',
align: 'left',
title: '币别',
width: 120
}
],
isTreeTable: false,
pagination: false,
useSearchForm: false,
showTableSetting: false,
bordered: false,
showIndexColumn: true,
canResize: false,
rowSelection: { type: 'checkbox' },
immediate: false
})
emitter.on('showTodo', (v) => {
if (v) {
const source = getDataSource()
const res = source.filter(item => {
return item.feeStatus == 1
})
setTableData(res)
} else {
}
})
//
const state = reactive({
historyRowKeys: []
})
const selectHandle = () => {
}
watch(
() => props.id,
(v) => {
if (v && v.length) {
reload()
} else {
setTableData([])
}
}
)
onMounted(() => {
if (props.id && props.id.length) {
//
reload()
}
})
defineExpose({
reload
})
</script>
<style lang="less">
.ds-approve-fee-table {
.ant-table-body {
height: 162px!important;
}
.ant-table-title {
min-height: 22px!important;
.items-center {
height: 22px;
}
.vben-basic-title {
font-size: 14px;
}
}
}
</style>

@ -0,0 +1,118 @@
<!--
* @Description: 审核审批 -> 主业务操作栏
* @Author: lijj
* @Date: 2024-06-13 17:08:08
-->
<template>
<div class="ds-approve-mian-action-bar">
<a-button type="link" @click="toggleSearch">{{ searchFlag? '' : '' }}</a-button>
<a-dropdown>
<template #overlay>
<a-menu>
<a-menu-item @click="approve(1)"></a-menu-item>
<a-menu-item @click="approve(2)"></a-menu-item>
<!-- <a-menu-item @click="approve(3)">()</a-menu-item>
<a-menu-item @click="approve(4)">()</a-menu-item> -->
</a-menu>
</template>
<a-button type="link">
批量审核
<span class="iconfont icon-xia" :style="{ fontSize: '12px' }"></span>
</a-button>
</a-dropdown>
<a-dropdown>
<template #overlay>
<a-menu>
<a-menu-item @click="lock(1)"></a-menu-item>
<a-menu-item @click="lock(2)"></a-menu-item>
<!-- <a-menu-item @click="approve(3)">()</a-menu-item>
<a-menu-item @click="approve(4)">()</a-menu-item> -->
</a-menu>
</template>
<a-button type="link">
费用锁定
<span class="iconfont icon-xia" :style="{ fontSize: '12px' }"></span>
</a-button>
</a-dropdown>
<a-checkbox v-model:checked="check">仅需审核业务</a-checkbox>
<a-checkbox v-model:checked="feeInfo">费用明细仅显示待审核</a-checkbox>
<a-button type="link">隐藏查询</a-button>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, defineProps, watch, defineEmits } from 'vue'
import { AuditByBiz } from '../api'
import emitter from '/@/utils/Bus'
import { useMessage } from '/@/hooks/web/useMessage'
const { createMessage } = useMessage()
const emit = defineEmits(['apporveHandle', 'update:searchFlag'])
const feeInfo = ref(false)
const check = ref(false)
const props = defineProps({
// id
ids: {
type: Array,
default: () => {
return []
}
},
data: {
type: Array,
default: () => {
return []
}
},
searchFlag: {
type: Boolean
}
})
//
const approve = (v, parent) => {
const postData = {
result: 1,
remark: "同意",
items: []
}
if (v == 1) {
//
if (props.ids.length == 0) return createMessage.warning('请选择要审核的业务!')
postData.items = props.ids
} else if (v == 2) {
//
postData.items = props.data.map(item => {
return { id: item?.id, businessType: item?.businessType }
})
}
emit('apporveHandle')
AuditByBiz(postData).then(res => {
if (res.succeeded) {
createMessage.success(res.message)
}
emit('apporveHandle')
}).catch(() => {
emit('apporveHandle')
})
}
//
const lock = () => {
createMessage.warning('开发中!')
}
const toggleSearch = () => {
emit('update:searchFlag', !props.searchFlag)
}
watch(
() => feeInfo.value,
(v) => {
emitter.emit('showTodo', v)
}
)
onMounted(() => {
})
</script>
<style lang="less">
.ds-approve-mian-action-bar {
}
</style>

@ -4,42 +4,78 @@
* @Date: 2024-06-13 17:08:08
-->
<template>
<a-spin :spinning="loading">
<div class="ds-approve-fee">
<BasicTable @register="registerTable">
<BasicTable
class="main-tb"
:scroll="{ x: '100%', y: '262px'}"
:showSearchForm='searchFlag'
@register="registerTable"
@selection-change="selectHandle"
>
<template #toolbar>
<MainActionBar
:ids="busId"
:data="getDataSource()"
v-model:searchFlag="searchFlag"
@apporveHandle="mainApprove"
></MainActionBar>
</template>
<template v-slot:bodyCell="{ column, record }">
<!-- 应收费用 -->
<template v-if="column.dataIndex == 'arFeeStatusText'">
<span v-if="record.arFeeStatus == 0" class="ds-green-tag">{{ record.arFeeStatusText }}</span>
<span v-else-if="record.arFeeStatus == 1" class="ds-blue-tag">{{ record.arFeeStatusText }}</span>
<span v-else-if="/^2|3|4|5|6$/.test(record.arFeeStatus)" class="ds-orange-tag">{{ record.arFeeStatusText }}</span>
<span v-else-if="/^7|8$/.test(record.arFeeStatus)" class="ds-red-tag">{{ record.arFeeStatusText }}</span>
<span v-else class="ds-purple-tag">{{ record.arFeeStatusText }}</span>
<span v-if="record.arFeeStatus == 0 || record.arFeeStatus == null" class="ds-grey-status"> {{ record.arFeeStatusText || '' }}</span>
<span v-else-if="record.arFeeStatus == 1" class="ds-blue-status"> {{ record.arFeeStatusText }}</span>
<span v-else-if="/^2|3|4|5|6$/.test(record.arFeeStatus)" class="ds-orange-status"> {{ record.arFeeStatusText }}</span>
<span v-else-if="/^7|8$/.test(record.arFeeStatus)" class="ds-red-status"> {{ record.arFeeStatusText }}</span>
<span v-else class="ds-purple-status"> {{ record.arFeeStatusText }}</span>
</template>
<!-- 应付费用 -->
<template v-if="column.dataIndex == 'apFeeStatusText'">
<span v-if="record.apFeeStatus == 0" class="ds-green-tag">{{ record.apFeeStatusText }}</span>
<span v-else-if="record.apFeeStatus == 1" class="ds-blue-tag">{{ record.apFeeStatusText }}</span>
<span v-else-if="/^2|3|4|5|6$/.test(record.apFeeStatus)" class="ds-orange-tag">{{ record.apFeeStatusText }}</span>
<span v-else-if="/^7|8$/.test(record.apFeeStatus)" class="ds-red-tag">{{ record.apFeeStatusText }}</span>
<span v-else class="ds-purple-tag">{{ record.apFeeStatusText }}</span>
<span v-if="record.apFeeStatus == 0 || record.arFeeStatus == null" class="ds-grey-status"> {{ record.apFeeStatusText || '' }}</span>
<span v-else-if="record.apFeeStatus == 1" class="ds-blue-status"> {{ record.apFeeStatusText }}</span>
<span v-else-if="/^2|3|4|5|6$/.test(record.apFeeStatus)" class="ds-orange-status"> {{ record.apFeeStatusText }}</span>
<span v-else-if="/^7|8$/.test(record.apFeeStatus)" class="ds-red-status"> {{ record.apFeeStatusText }}</span>
<span v-else class="ds-purple-status"> {{ record.apFeeStatusText }}</span>
</template>
</template>
</BasicTable>
<div class="fee-box">
<!-- 应收费用表格 -->
<FeeTable
class="fee-tb"
ref="receiveTable"
:id="busId"
tbType="receive"
></FeeTable>
<!-- 应付费用表格 -->
<FeeTable
class="fee-tb mt5"
ref="payTable"
:id="busId"
tbType="pay"
></FeeTable>
</div>
</div>
</a-spin>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive } from 'vue'
import { onMounted, ref, reactive, watch, defineExpose } from 'vue'
import { BasicTable, useTable } from '/@/components/Table'
import { GetList } from './api'
import { useModal } from '/@/components/Modal'
import { columns, searchFormSchema } from './columns'
import { useMessage } from '/@/hooks/web/useMessage'
import FeeTable from './components/feeTable.vue'
import MainActionBar from './components/mainActionBar.vue'
const { notification } = useMessage()
//
const [registerModal, { openModal }] = useModal()
const [registerTable, { reload, getForm, getPaginationRef, getSelectRows }] = useTable({
// loading
const loading = ref(false)
// id
const busId = ref([])
// search
const searchFlag = ref(true)
const [registerTable, { reload, getForm, getPaginationRef, getDataSource, redoHeight }] = useTable({
title: '',
api: async (p) => {
const res = await GetList(p)
@ -176,11 +212,11 @@
},
isTreeTable: false,
pagination: true,
striped: true,
useSearchForm: true,
striped: false,
useSearchForm: searchFlag.value,
showTableSetting: true,
bordered: true,
showIndexColumn: true,
rowSelection: { type: 'checkbox' },
canResize: false,
immediate: false
})
@ -188,28 +224,59 @@
const state = reactive({
historyRowKeys: []
})
const selectHandle = (v) => {
let ids = []
if (v.rows && v.rows.length) {
ids = v.rows.map(item => {
return { id: item.id, businessType: item.businessType }
})
}
busId.value = ids
}
const receiveTable = ref(null)
const payTable = ref(null)
const mainApprove = () => {
loading.value = !loading.value
if (!loading.value) {
receiveTable.value.reload()
payTable.value.reload()
}
}
onMounted(() => {
//
reload()
const a = redoHeight()
console.log(a)
})
defineExpose({
getDataSource
})
</script>
<style lang="less">
.ds-sea-export-table {
.ds-approve-fee {
display: flex;
height: calc(100vh - 85px);
flex-direction: column;
.fee-tb {
height: 230px;
}
.fee-box {
height: 465px;
overflow: hidden;
padding: 0 8px 8px 8px;
}
.main-tb {
overflow: auto;
flex: 1;
}
.vben-basic-table-header__toolbar {
justify-content: space-between;
}
.mblno {
text-align: left;
color: @primary-color !important;
span {
margin-right: 0.5rem;
cursor: pointer;
}
.CSMblnoCopy {
cursor: pointer;
.ant-table-title {
min-height: 22px!important;
.items-center {
height: 22px;
}
}
}

@ -6,7 +6,9 @@
<template>
<div>
<BasicTable @register="registerTable">
<BasicTable
@register="registerTable"
>
<template #toolbar>
<a-button type="primary" @click="handleCreate"> </a-button>
</template>

Loading…
Cancel
Save