费用审核

szh-new
lijingjia 5 months ago
parent a145108480
commit 8e6812a539

@ -4,8 +4,6 @@ import { FormSchema } from '/@/components/Table'
import { getDictOption } from '/@/utils/dictUtil'
// 下拉框数据接口
import { GetFeeCurrencySelectList, GetClientListByCode, GetFeeCodeSelectList } from '/@/api/common'
// 客户类别下拉框数据
const customTypeDict = ref([])
// 往来单位下拉框数据
const companyDict = ref([])
// 引入计费标准字典
@ -70,16 +68,20 @@ export const formSchema: FormSchema[] = [
field: 'customerType',
label: '客户类别',
defaultValue: '',
component: 'Select',
component: 'ApiSelect',
colProps: { span: 5 },
componentProps: ({ formModel }) => {
getDictOption('djy_cust_prop').then(data => {
customTypeDict.value = data
})
return {
options: customTypeDict.value,
allowClear: true,
showSearch: true,
api: () => {
return new Promise((resolve) => {
getDictOption('djy_cust_prop').then((res) => {
resolve(res)
})
})
},
labelField: 'label',
valueField: 'value',
resultField: 'data',
filterOption: (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
},

@ -4,14 +4,35 @@
* @Description: 流程图组件
-->
<template>
<a-modal
<a-modal
class="ds-flow-chart"
v-model:visible="visible"
v-if="visible"
title="流程图"
width="80%"
@ok="handleOk"
:footer="null"
width="60%"
>
<FlowDesign :process="process" :fields="fields" :readonly="true" />
<template #title>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="审批历史">
<el-timeline>
<el-timeline-item
placement="top"
>
<div>
</div>
</el-timeline-item>
</el-timeline>
</a-tab-pane>
<a-tab-pane key="2" tab="流程图" force-render>
<div class="ds-flow-status">
<span></span> 已通过
<span class="ml10"></span> 已驳回
</div>
<FlowDesign :process="process" :fields="[]" :readonly="true" />
</a-tab-pane>
</a-tabs>
</template>
</a-modal>
</template>
@ -19,35 +40,64 @@
import { ref, defineExpose } from 'vue'
import FlowDesign from '/@/views/flowcenter/flowInstances/Lowflow/index.vue'
import { GetFlowContent } from './api'
import { GetFlowInstanceHistoryList } from '/@/views/flowcenter/flowInstances/api'
const visible = ref(false)
const process = ref({})
const activeKey = ref('2')
const init = (id, type, btype) => {
visible.value = true
console.log(id)
GetFlowInstanceHistoryList({ id }).then(res => {
console.log(res)
})
GetFlowContent({ businessId: id, type, businessType: btype }).then(res => {
console.log(res.Data)
process.value = JSON.parse(res.data)
})
}
const fields = ref([])
const process = ref({
id: 'root',
pid: null,
type: 'start',
name: '发起人',
formProperties: [],
child: {
id: 'end',
pid: 'root',
type: 'end',
name: '结束',
child: null,
}
})
const handleOk = () => {
}
defineExpose({
init
})
</script>
<style>
<style lang="less">
.ds-flow-chart{
.ant-modal-header {
border-bottom: none!important;
}
.ant-tabs-nav {
margin-bottom: 0;
}
.ant-modal-title {
position: relative;
top: -15px;
}
.ant-modal-body {
display: none;
}
.designer-container {
padding-top: 30px;
overflow: auto;
}
.ant-tabs-content {
min-height: 500px;
}
.ds-flow-status {
position: absolute;
top: 65px;
z-index: 10;
font-weight: 400!important;
span {
display: inline-block;
width: 14px;
height: 14px;
border-radius: 3px;
position: relative;
top: 2px;
}
span:first-child {
border: 1px solid rgb(4, 185, 19);
}
span:last-child {
border: 1px solid red;
}
}
}
</style>

@ -51,7 +51,7 @@
}
.ant-modal {
top: 30px!important;
// top: 30px!important;
}
// 自定义最小输入框

@ -3,7 +3,10 @@ import { request } from '/@/utils/request'
import { DataResult, PageRequest } from '/@/api/model/baseModel'
enum Api {
GetList = '/feeApi/FeeAudit/GetList',
AuditByBiz = '/feeApi/FeeAudit/AuditByBiz'
AuditByBiz = '/feeApi/FeeAudit/AuditByBiz',
Audit = '/feeApi/FeeAudit/Audit',
SetInvoiceEnabled = '/feeApi/FeeRecord/SetInvoiceEnabled',
GetFees = '/feeApi/FeeAudit/GetFees'
}
// 业务列表
@ -23,6 +26,34 @@ export function AuditByBiz(data: PageRequest) {
data
})
}
// 费用审核
export function Audit(data: PageRequest) {
return request<DataResult>({
url: Api.Audit,
method: 'post',
data
})
}
// 费用禁(解)开发票
export function SetInvoiceEnabled(data: PageRequest) {
return request<DataResult>({
url: Api.SetInvoiceEnabled,
method: 'post',
data
})
}
// 获取费用明细
export function GetFees(data: PageRequest) {
return request<DataResult>({
url: Api.GetFees,
method: 'post',
data
})
}
// export function getThermometryWarningCount() {
// return request<DataResult>({
// url: Api.getThermometryWarningCount,

@ -6,7 +6,15 @@
import { ref } from 'vue'
import { BasicColumn, FormSchema } from '/@/components/Table'
import { getDictOption } from '/@/utils/dictUtil'
import { GetVesselSelectList, GetVoynoSelectList, GetAgentClientList, GetSaleList } from '/@/views/operation/seaexport/api/BookingLedger'
import {
GetVesselSelectList,
GetVoynoSelectList,
GetAgentClientList,
GetSaleList,
GetControllerClientList,
GetClientSourceSelectList
} from '/@/views/operation/seaexport/api/BookingLedger'
import { GetFeeCodeSelectList } from '/@/api/common'
// 结算对象下拉数据
import { GetClientListByCode } from '/@/api/common'
export const searchFormSchema: FormSchema[] = [
@ -137,6 +145,75 @@ export const searchFormSchema: FormSchema[] = [
resultField: 'data'
}
}
},
{
label: '委托单位',
field: 'customer',
component: 'ApiSelect',
required: false,
dynamicDisabled: false,
colProps: { span: 4 },
componentProps: () => {
return {
api: GetControllerClientList,
immediate: false,
labelField: 'shortName',
valueField: 'id',
resultField: 'data',
filterOption: (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
}
}
},
{
field: 'etd',
label: '业务日期',
component: 'RangePicker',
required: false,
dynamicDisabled: false,
colProps: { span: 4 },
componentProps: {
allowClear: true,
}
},
{
field: 'feeCode',
label: '费用名称',
defaultValue: '',
component: 'ApiSelect',
colProps: { span: 4 },
componentProps: () => {
return {
api: GetFeeCodeSelectList,
labelField: 'name',
immediate: false,
valueField: 'code',
resultField: 'data',
filterOption: (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
}
}
},
{
field: 'sourceId',
label: '业务来源',
component: 'ApiSelect',
colProps: { span: 4 },
defaultValue: '',
componentProps: () => {
return {
api: GetClientSourceSelectList,
labelField: 'sourceName',
immediate: false,
valueField: 'id',
resultField: 'data',
filterOption: (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
}
}
}
]

@ -5,31 +5,188 @@
-->
<template>
<div class="ds-approve-mian-action-bar">
<a-button type="link">
<span class="iconfont icon-chenggong"></span>
<a-button type="link" @click="agree('1', 1)">
<span class="iconfont icon-chenggong" :style="{fontSize: '13px'}"></span>
审核通过
</a-button>
<a-button type="link">
<span class="iconfont icon-xiajiantou"></span>
<span class="iconfont icon-lianjie" :style="{fontSize: '13px'}"></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>
<a-button type="link" @click="reject">
<span class="iconfont icon-return" :style="{fontSize: '13px'}"></span>
驳回提交
</a-button>
<a-button :loading="kloading" type="link" @click="invoiceOp(0)"></a-button>
<a-button :loading="jloading" type="link" @click="invoiceOp(1)"></a-button>
<a-button type="link" @click="agree('2', 1)">批准申请</a-button>
<a-button type="link" @click="agree('3', 2)">驳回申请</a-button>
<!-- <a-button type="link">历史申请</a-button> -->
<!-- <a-button type="link">查看信息</a-button> -->
<a-button type="link" @click="openFlowChart"></a-button>
<a-modal v-model:visible="visible" title="提醒" :confirm-loading="confirmLoading" @ok="handleOk">
<p>{{ profit < 0 ? '该票业务利润小于0确定审核通过吗?' : '该票业务利润等于0确定审核通过吗?' }}</p>
</a-modal>
<!-- 费用驳回申请弹窗 -->
<FeeRejectModal
:ids="ids"
:data="data"
@success="handleSuccess"
@register="registerModal"
/>
<!-- 流程图 -->
<FlowChart ref="flowChart"></FlowChart>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, defineProps, watch, defineEmits } from 'vue'
import { AuditByBiz } from '../api'
import FlowChart from '/@/components/FlowChart/index.vue'
import FeeRejectModal from './feeRejectModal.vue'
import { useModal } from '/@/components/Modal'
import { Audit, SetInvoiceEnabled } from '../api'
import { useMessage } from '/@/hooks/web/useMessage'
const { createMessage } = useMessage()
const props = defineProps({
// id
ids: {
type: Array,
default: () => {
return []
}
},
//
profit: {
type: Number,
default: 0
},
//
data: {
type: Array,
default: () => {
return []
}
},
//
feeStatus: {
type: Array,
default: () => {
return []
}
}
})
const emit = defineEmits(['checkProfit', 'refresh'])
const visible = ref(false)
const rejectFlag = ref(false)
const confirmLoading = ref(false)
// 0
const handleOk = () => {
approve(1, 1)
}
// params(v: 1.2.3.), (r: 1.2.)
const agree = async(v, r) => {
if (props.ids.length == 0) return createMessage.warning('请选择要审核的费用!')
if (v == '3' || v == '2') {
for(let i = 0; i < props.feeStatus.length; i++) {
if (props.feeStatus[i] == 2) {
if (v == 2) {
return createMessage.warning('只能费用状态为[申请修改]或[申请删除]的费用才能审核通过!')
} else {
return createMessage.warning('只能费用状态为[申请修改]或[申请删除]的费用才能驳回提交!')
}
}
}
}
await emit('checkProfit')
// 0
if (props.profit <= 0 && (v == '1' || v == '2')) {
visible.value = true
return
}
//
if (v == '3') {
openModal(true, {
businessType: props?.data[0]?.businessType,
ids: props.ids
})
return
}
approve(2, r)
}
const approve = (v, r) => {
const postData = {
result: r,
remark: r == 1 ? "同意" : '',
ids: props.ids,
businessType: null
}
postData.businessType = props?.data[0]?.businessType
confirmLoading.value = true
Audit(postData).then(res => {
confirmLoading.value = false
if (v == 1) {
visible.value = false
}
if (res.succeeded) {
createMessage.success(res.message)
emit('refresh')
}
}).catch(() => {
confirmLoading.value = false
})
}
const [registerModal, { openModal }] = useModal()
//
const reject = () => {
if (props.ids.length == 0) return createMessage.warning('请选择要审核的费用!')
openModal(true, {
businessType: props?.data[0]?.businessType,
ids: props.ids
})
}
//
const handleSuccess = () => {
emit('refresh')
}
//
const kloading = ref(false)
const jloading = ref(false)
const invoiceOp = (v) => {
if (props.ids.length == 0) return createMessage.warning(`请选择要${v == 1 ? '解' : ''}禁开发票的费用!`)
const postData = {
ids: props.ids,
businessType: props?.data[0]?.businessType,
remark: v == 1 ? '解禁开发票' : '禁开发票',
value: v
}
if (v == 1) {
jloading.value = true
} else {
kloading.value = true
}
SetInvoiceEnabled(postData).then(res => {
jloading.value = false
kloading.value = false
if (res.succeeded) {
createMessage.success(res.message)
emit('refresh')
}
}).catch((err) => {
jloading.value = false
kloading.value = false
createMessage.warning(err.message)
})
}
//
const flowChart = ref(null)
//
const openFlowChart = () => {
if (props.ids.length == 0) return createMessage.warning('请勾选要查看流程的费用!')
if (props.ids.length > 1) return createMessage.warning('仅支持勾选1条费用进行查看流程')
if (props.feeStatus[0] == 1) return createMessage.warning('费用录入状态的数据不支持查看流程!')
console.log(props.ids)
flowChart.value.init(props.ids[0], 0, 1)
}
onMounted(() => {
})
</script>

@ -0,0 +1,112 @@
<!--
* @Description: 审核审批 -> 费用提交驳回窗口
* @Author: lijj
* @Date: 2024-04-29 11:53:37
-->
<template>
<BasicModal
v-bind="$attrs"
:use-wrapper="true"
title="驳回提交"
width="50%"
@register="registerModal"
@ok="handleSave"
>
<!-- 表单 -->
<BasicForm @register="registerForm" class="reject-form">
</BasicForm>
<!--右下角按钮-->
<template #footer>
<a-button
pre-icon="ant-design:close-outlined"
type="warning"
:loading="loading"
ghost
style="margin-right: 0.8rem"
@click="closeModal"
>
取消
</a-button>
<a-button
pre-icon="ant-design:check-circle-outlined"
type="primary"
:loading="loading"
@click="handleSave(true)"
>
确定
</a-button>
</template>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive } from 'vue'
import { Audit } from '../api'
import { BasicModal, useModalInner } from '/@/components/Modal'
import { BasicForm, useForm } from '/@/components/Form/index'
import { useMessage } from '/@/hooks/web/useMessage'
const { createMessage } = useMessage()
const formSchema = [
{
field: 'reason',
label: '驳回原因',
defaultValue: '',
component: 'InputTextArea',
required: true,
colProps: { span: 24 },
componentProps: {
rows: 4
}
}
]
// Emits
const emit = defineEmits(['success'])
const loading = ref(false)
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] =
useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
})
const params = reactive({
ids: null,
businessType: null
})
const [registerModal, { setModalProps, closeModal, updateFormField }] = useModalInner(async (data) => {
resetFields()
console.log(data.ids)
params.ids = data.ids
params.businessType = data.businessType
})
async function handleSave(exit) {
try {
const values = await validate()
loading.value = true
setModalProps({ confirmLoading: true, loading: true })
const postData = {
result: 2,
remark: values.reason,
ids: params.ids,
businessType: params.businessType
}
const res = await Audit(postData)
loading.value = false
if (res.succeeded) {
createMessage.success(res.message)
emit('success')
}
exit && closeModal()
} finally {
setModalProps({ confirmLoading: false, loading: false })
}
}
</script>
<style lang="less">
.reject-form {
#form_item_reason {
height: 170px;
}
}
</style>

@ -12,6 +12,12 @@
>
<template #toolbar>
<FeeActionBar
:ids="feeIds"
:feeStatus="feeStatus"
:profit="profit"
:data="data"
@refresh="reload"
@checkProfit="checkProfit"
></FeeActionBar>
</template>
<template v-slot:bodyCell="{ column, record }">
@ -24,10 +30,10 @@
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, defineProps, watch, defineExpose } from 'vue'
import { onMounted, ref, reactive, defineProps, watch, defineExpose, defineEmits } from 'vue'
import { BasicTable, useTable } from '/@/components/Table'
import { useMessage } from '/@/hooks/web/useMessage'
import { GetList } from '/@/components/CostEntry/api'
import { GetFees } from '../api'
import FeeActionBar from './feeActionBar.vue'
import emitter from '/@/utils/Bus'
const { notification } = useMessage()
@ -43,13 +49,25 @@
tbType: {
type: String,
default: null
},
//
profit: {
type: Number,
default: 0
}
})
const emit = defineEmits(['checkProfit'])
const checkProfit = () => {
return emit('checkProfit')
}
const data = ref([])
//
const oldData = ref([])
const [registerTable, { reload, setTableData, getDataSource, getSelectRows }] = useTable({
title: props.tbType == 'receive' ? '应收费用' : '应付费用',
striped: false,
api: async (p) => {
const res = await GetList(p)
const res = await GetFees(p)
return new Promise((resolve) => {
res.data.forEach(item => {
// item.stlDate = formatTableData(item.stlDate)
@ -60,20 +78,18 @@
// item.customDate = formatTableData(item.customDate)
// item.inspectionDate = formatTableData(item.inspectionDate)
})
data.value = res.data
oldData.value = JSON.parse(JSON.stringify(res.data))
resolve({ data: [...res.data], total: res.count })
})
},
beforeFetch: () => {
const postParam = {
pageCondition: {
pageIndex: 1,
pageSize: 1000,
sortConditions: []
},
id: props.id[props.id.length - 1]?.id,
businessType: props.id[props.id.length - 1]?.businessType,
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 }
// { FieldName: 'BusinessId', FieldValue: props.id[props.id.length - 1]?.id, ConditionalType: 1 },
{ FieldName: 'FeeType', FieldValue: props.tbType == 'receive' ? 1 : 2, ConditionalType: 1 }
])
}
return postParam
@ -178,15 +194,27 @@
})
setTableData(res)
} else {
setTableData(oldData.value)
}
})
//
const state = reactive({
historyRowKeys: []
})
const selectHandle = () => {
// id
const feeIds = ref()
//
const feeStatus = ref()
//
const selectHandle = (v) => {
let ids = []
let status = []
if (v.rows && v.rows.length) {
ids = v.rows.map(item => {
return item.id
})
status = v.rows.map(item => {
return item.feeStatus
})
}
feeStatus.value = status
feeIds.value = ids
}
watch(
() => props.id,
@ -205,7 +233,8 @@
}
})
defineExpose({
reload
reload,
getDataSource
})
</script>

@ -36,7 +36,6 @@
</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>

@ -16,7 +16,7 @@
<template #toolbar>
<MainActionBar
:ids="busId"
:data="getDataSource()"
:data="data"
v-model:searchFlag="searchFlag"
@apporveHandle="mainApprove"
></MainActionBar>
@ -46,14 +46,18 @@
class="fee-tb"
ref="receiveTable"
:id="busId"
:profit="profit"
tbType="receive"
@checkProfit="checkProfit"
></FeeTable>
<!-- 应付费用表格 -->
<FeeTable
class="fee-tb mt5"
ref="payTable"
:id="busId"
:profit="profit"
tbType="pay"
@checkProfit="checkProfit"
></FeeTable>
</div>
</div>
@ -75,6 +79,7 @@
const busId = ref([])
// search
const searchFlag = ref(true)
const data = ref([])
const [registerTable, { reload, getForm, getPaginationRef, getDataSource, redoHeight }] = useTable({
title: '',
api: async (p) => {
@ -89,6 +94,7 @@
// item.customDate = formatTableData(item.customDate)
// item.inspectionDate = formatTableData(item.inspectionDate)
})
data.value = res.data
resolve({ data: [...res.data], total: res.count })
})
},
@ -220,10 +226,6 @@
canResize: false,
immediate: false
})
//
const state = reactive({
historyRowKeys: []
})
const selectHandle = (v) => {
let ids = []
if (v.rows && v.rows.length) {
@ -231,6 +233,7 @@
return { id: item.id, businessType: item.businessType }
})
}
//
busId.value = ids
}
const receiveTable = ref(null)
@ -242,6 +245,22 @@
payTable.value.reload()
}
}
//
const profit = ref(0)
//
const checkProfit = () => {
const receiveData = receiveTable.value.getDataSource()
const payData = payTable.value.getDataSource()
let rAmount = 0
receiveData.forEach(item => {
rAmount = rAmount + Number(item.amount )
})
let pAmount = 0
payData.forEach(item => {
pAmount = pAmount + Number(item.amount )
})
profit.value = rAmount - pAmount
}
onMounted(() => {
//
reload()
@ -279,6 +298,9 @@
height: 22px;
}
}
.ant-table-body {
height: calc(100vh - 755px)!important;
}
}
.high-search {
.CsConditionFilter {

@ -44,9 +44,9 @@
ApiData: propTypes.object,
fields: propTypes.array,
})
//
const fields = ref<Field[]>([...props.fields])
console.log(fields)
watch(
() => props.ApiData,
() => {

@ -16,7 +16,7 @@
EditPen,
CircleClose,
Share,
WarnTriangleFilled,
WarnTriangleFilled
} from '@element-plus/icons-vue'
import { useAppStore } from '/@/store/modules/app'
export interface NodeProps {
@ -169,6 +169,7 @@
'node',
{ 'error-node': $props.errorInfo.showError },
previousId == node.id ? 'CurrentType' : '',
node.setInfo ? (node?.setInfo?.taged == 1 ? 'ds-flow-agree' : 'ds-flow-refuse' ) : ''
]"
@mouseenter="mouseenter"
@mouseleave="mouseleave"
@ -238,6 +239,12 @@
</template>
<style scoped lang="scss">
.ds-flow-agree {
border: 2px solid rgb(4, 185, 19);
}
.ds-flow-refuse {
border: 2px solid #e60000;
}
.node-box {
position: relative;
display: flex;

Loading…
Cancel
Save