费用录入

szh-new
lijingjia 6 months ago
parent 6499f22337
commit 8b38d770ab

@ -13,19 +13,36 @@
<span class="iconfont icon-fuzhi" :style="{ fontSize: '14px' }"></span>
复制
</a-button>
<a-button type="link">
<a-button type="link" @click="save">
<span class="iconfont icon-icon_baocun" :style="{ fontSize: '14px' }"></span>
保存
</a-button>
<a-button danger type="link" @click="deleteRow">
<span class="iconfont icon-shanchu1" :style="{ fontSize: '14px' }"></span>
删除
</a-button>
<a-button type="link">
<span class="iconfont icon-shanchu" :style="{ fontSize: '14px' }"></span>
取消
</a-button>
<a-button type="link">
<a-popconfirm
:visible="deleteFlag"
title="确定要删除勾选的数据?"
ok-text="确定"
cancel-text="取消"
@confirm="deleteRow"
@cancel="cancelDelete"
@click="checkDelete"
>
<a-button danger type="link" >
<span class="iconfont icon-shanchu1" :style="{ fontSize: '14px' }"></span>
删除
</a-button>
</a-popconfirm>
<a-popconfirm
title="是否取消数据录入?"
ok-text="确定"
cancel-text="取消"
@confirm="cancelEdit"
>
<a-button danger type="link">
<span class="iconfont icon-shanchu" :style="{ fontSize: '14px' }"></span>
取消
</a-button>
</a-popconfirm>
<a-button type="link" @click="refresh">
<span class="iconfont icon-shuaxin" :style="{ fontSize: '14px' }"></span>
刷新
</a-button>
@ -45,8 +62,10 @@
</div>
</template>
<script lang="ts" setup>
import { defineEmits, defineProps } from 'vue'
const emits = defineEmits(['addRow', 'copyRow'])
import { defineEmits, defineProps, ref, onMounted, watch } from 'vue'
import { useMessage } from '/@/hooks/web/useMessage'
const { createMessage } = useMessage()
const emits = defineEmits(['save', 'delete', 'cancel', 'refresh'])
const props = defineProps({
//
data: {
@ -60,6 +79,8 @@
default: {}
}
})
//
const deleteFlag = ref(false)
//
const addRow = () => {
const deepCopyRow = JSON.parse(JSON.stringify(props.row))
@ -73,14 +94,49 @@
needCopy.forEach(item => {
item.selected = false
const deepCopyRow = JSON.parse(JSON.stringify(item))
deepCopyRow.id = ''
deepCopyRow.feeStatus = 1
props.data.push(deepCopyRow)
})
}
//
const checkDelete = () => {
let flag = false
const arr = []
props.data.forEach(item => {
if (item?.selected) flag = true
if (item?.selected && (item?.feeStatus != 1 && item?.feeStatus != 6)) {
arr.push(item)
}
})
if (!flag) {
return createMessage.warning('请勾选要产出的数据!')
}
if (arr.length) {
return createMessage.warning('只能删除费用状态为[录入状态]和[驳回提交]的费用,请重新选择!')
}
deleteFlag.value = true
}
//
const deleteRow = () => {
props.data.forEach((item, index) => {
if (item.selected) props.data.splice(index, 1)
})
deleteFlag.value = false
emits('delete')
}
//
const cancelDelete = () => {
deleteFlag.value = false
}
//
const save = () => {
emits('save')
}
//
const cancelEdit = () => {
emits('cancel')
}
//
const refresh = () => {
emits('refresh')
}
</script>

@ -7,12 +7,13 @@
import { request } from '/@/utils/request'
import { DataResult, PageRequest } from '/@/api/model/baseModel'
enum Api {
list = '/feeApi/FeeRecord/GetFeeRecordList',
edit = '/feeApi/FeeRecord/EditFeeRecord',
info = '/feeApi/FeeRecord/GetFeeRecordInfo'
list = '/feeApi/FeeRecord/GetList',
edit = '/feeApi/FeeRecord/Submit',
delete = '/feeApi/FeeRecord/Delete',
statistic = '/feeApi/FeeRecord/FeeStatistics'
}
// 列表 (Auth)
export function GetFeeRecordList(data: PageRequest) {
export function GetList(data: PageRequest) {
return request<DataResult>({
url: Api.list,
method: 'post',
@ -20,7 +21,7 @@ export function GetFeeRecordList(data: PageRequest) {
})
}
// 编辑 (Auth)
export function EditFeeRecord(data: PageRequest) {
export function SubmitFee(data: PageRequest) {
return request<DataResult>({
url: Api.edit,
method: 'post',
@ -28,11 +29,19 @@ export function EditFeeRecord(data: PageRequest) {
})
}
// 详情 (Auth)
export function GetFeeRecordInfo(query) {
export function DeleteFee(data) {
return request<DataResult>({
url: Api.info,
method: 'get',
params: query
url: Api.delete,
method: 'post',
data
})
}
// 统计
export function FeeStatistics(data: PageRequest) {
return request<DataResult>({
url: Api.statistic,
method: 'post',
data
})
}

@ -0,0 +1,167 @@
<!--
* @Description: 费用录入统计组件
* @Author: lijj
* @Date: 2024-05-07 15:19:07
-->
<template>
<div class="fee-statistic">
<a-collapse>
<a-collapse-panel>
<template v-slot:header>
<p>
<span class="title">利润合计</span>
<span class="title">RMB应收: </span>
<span class="count" :class="{ warnText: data.receivableCNY < 0 }">{{ data.receivableCNY }}</span>
<span class="title">RMB应付: </span>
<span class="count" :class="{ warnText: data.payableCNY < 0 }">{{ data.payableCNY }}</span>
<span class="title">RMB利润: </span>
<span class="count" :class="{ warnText: data.profitCNY < 0 }">{{ data.profitCNY }}</span>
<span class="line"></span>
<span class="title">USD应收: </span>
<span class="count" :class="{ warnText: data.receivableUSD < 0 }">{{ data.receivableUSD }}</span>
<span class="title">USD应付: </span>
<span class="count" :class="{ warnText: data.payableUSD < 0 }">{{ data.payableUSD }}</span>
<span class="title">USD利润: </span>
<span class="count" :class="{ warnText: data.profitUSD < 0 }">{{ data.profitUSD }}</span>
<span class="line"></span>
<span class="title">合计应收: </span>
<span class="count" :class="{ warnText: data.receivableTotal < 0 }">{{ data.receivableTotal }}</span>
<span class="title">合计应付: </span>
<span class="count" :class="{ warnText: data.payableTotal < 0 }">{{ data.payableTotal }}</span>
<span class="title">合计利润: </span>
<span class="count" :class="{ warnText: data.profitTotal < 0 }">{{ data.profitTotal }}</span>
<span class="title">利润率: </span>
<span class="count" :class="{ warnText: data.profitUSD < 0 }">{{ data.profitUSD }}%</span>
</p>
</template>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="客户合计">
<BasicTable @register="registerTable"></BasicTable>
</a-tab-pane>
<a-tab-pane key="2" tab="币别合计" force-render>
</a-tab-pane>
</a-tabs>
</a-collapse-panel>
</a-collapse>
</div>
</template>
<script lang="ts" setup>
import { BasicTable, useTable } from '/@/components/Table'
import { onMounted, ref } from 'vue'
import { FeeStatistics } from './api'
//
const data = ref({})
//
const activeKey = ref('1')
const init = () => {
const postData = {
queryCondition: JSON.stringify([
{ FieldName: 'BusinessId', FieldValue: '1780891904372772864', ConditionalType: 1 }
])
}
FeeStatistics(postData).then(res => {
data.value = res.data
})
}
onMounted(() => {
init()
})
const [registerTable, { reload, getForm, getPaginationRef }] = useTable({
title: '',
api: async () => {
return new Promise((resolve) => {
resolve({ data: [...data.value.byCustomers] })
})
},
columns: [
{
title: '结算对象',
dataIndex: 'customerName',
width: 150
}, {
title: 'RMB应收',
dataIndex: 'receivableCNY',
width: 150
}, {
title: 'RMB应付',
dataIndex: 'payableCNY',
width: 150
}, {
title: 'USD应收',
dataIndex: 'receivableUSD',
width: 150
}, {
title: 'USD应付',
dataIndex: 'payableUSD',
width: 150
}, {
title: '合计应收',
dataIndex: 'receivableTotal',
width: 150
}, {
title: '合计应付',
dataIndex: 'payableTotal',
width: 150
}
],
isTreeTable: false,
pagination: true,
striped: true,
bordered: true,
showIndexColumn: true,
canResize: false
})
</script>
<style lang="scss">
.fee-statistic {
position: fixed;
bottom: 0;
background: #ffffff;
z-index: 199;
.title {
font-size: 12px;
font-weight: 600;
color: #04408c;
margin-left: 10px;
}
.count {
color: rgb(0, 128, 0);
font-weight: 600;
font-size: 12px;
}
.warnText {
color: red;
}
.line {
display: inline-block;
height: 10px;
width: 2px;
background: #04408c;
margin: 0 5px 0 15px;
}
.ant-collapse > .ant-collapse-item > .ant-collapse-header {
padding: 5px 15px;
p {
margin-bottom: 0;
}
}
.ant-collapse-content-box {
padding-top: 0;
}
.ant-table-cell {
padding: 5px 10px!important;
}
.ant-tabs-nav {
margin-bottom: 0;
}
.ant-table-wrapper {
padding-left: 0;
}
.ant-tabs-tab {
padding: 8px;
}
}
</style>

@ -1,22 +1,28 @@
<!--
* @Description: 费用录入布局组件
* @Description: 费用录入应收组件
* @Author: lijj
* @Date: 2024-05-07 15:19:07
-->
<template>
<div class="cost-entry-receive">
<div class="flex">
<h2>应收</h2>
<ActionBar
:data="list"
:row="row"
></ActionBar>
<a-spin :spinning="loading">
<div class="cost-entry-receive">
<div class="flex">
<h2>{{ tbType == 'receive' ? '应收' : '应付' }}</h2>
<ActionBar
:data="list"
:row="row"
@save="save"
@delete="deleteRow"
@cancel="cancelEdit"
@refresh="init"
></ActionBar>
</div>
<div>
<input class="ds-tb-check" type="checkbox" v-model="allCheck" :indeterminate="someCheck">
<hot-table ref="hotTb" :data="list" :settings="settings"></hot-table>
</div>
</div>
<div>
<input class="ds-tb-check" type="checkbox" v-model="allCheck" :indeterminate="someCheck">
<hot-table ref="hotTb" :data="list" :settings="settings"></hot-table>
</div>
</div>
</a-spin>
</template>
<script lang="ts" setup>
import { defineProps, ref, watch, watchEffect, computed, unref, defineComponent, onMounted, reactive, nextTick } from 'vue'
@ -24,17 +30,25 @@
import { GetFeeCurrencySelectList, GetFeeCodeSelectList } from '/@/api/common'
import { HotTable } from '@handsontable/vue3';
import { registerAllModules } from 'handsontable/registry';
defineComponent({
HotTable
})
import 'handsontable/dist/handsontable.full.min.css';
registerAllModules();
//
import ActionBar from './actionBar.vue'
// import { columns } from './columns/receive'
//
import { feeUnitDict } from '/@/hooks/dict/index'
import { GetFeeRecordList } from './api'
import { GetList, SubmitFee, DeleteFee } from './api'
import { useMessage } from '/@/hooks/web/useMessage'
const { createMessage } = useMessage()
defineComponent({
HotTable
})
registerAllModules()
const props = defineProps({
tbType: {
type: String,
default: ''
}
})
//
const feeDict = ref([])
//
@ -45,21 +59,28 @@
import { getDictOption } from '/@/utils/dictUtil'
//
const hotTb = ref(null)
// loading
const loading = ref(false)
//
const row = {
selected: false,
feeStatus: '1',
feeStatusText: '录入状态',
id: '',
businessId: '',
feeType: '',
feeCode: '',
feeName: '',
feeEnName: ''
}
//
const list = ref([])
//
let oldData = []
//
const allCheck = ref(false)
//
const someCheck = ref(false)
const feeStatusList = ['审核通过', '录入状态', '提交审核', '申请修改', '申请删除', '撤销申请', '驳回提交', '驳回申请', '部分结算', '结算完毕']
//
const columns = [
{
@ -81,7 +102,7 @@
}, {
title: '费用状态',
width: 120,
data: 'feeStatus',
data: 'feeStatusText',
readOnly: true
}, {
title: '应收费用名称',
@ -113,7 +134,7 @@
}, {
title: '客户类别',
width: 130,
data: 'customerTypeName',
data: 'customerTypeText',
type: 'dropdown',
source: async (query, process) => {
const results = await getDictOption('djy_cust_prop')
@ -125,7 +146,7 @@
}, {
title: '单位标准',
width: 130,
data: 'unitName',
data: 'unitText',
type: 'dropdown',
source: async (query, process) => {
if (unitDict.value && unitDict.value.length) {
@ -186,24 +207,112 @@
data: 'note'
}, {
title: '币别',
width: 120,
data: 'currencyName',
width: 80,
data: 'currency',
type: 'dropdown',
source: async (query, process) => {
if (currencyDict.value.length) {
const dict = currencyDict.value?.map(res => {
return res.name
})
process(dict)
process(currencyDict.value)
} else {
const results = await GetFeeCurrencySelectList()
const dict = results.data?.map(res => {
return res.name
return res.codeName
})
currencyDict.value = results.data
currencyDict.value = dict
process(dict)
}
}
}, {
title: '销项税率',
width: 100,
data: 'accTaxRate',
type: 'numeric'
}, {
title: '销项税额',
width: 100,
data: 'accTaxAmount',
readOnly: true
}, {
title: '销项金额',
width: 100,
data: 'accAmount',
readOnly: true
}, {
title: '是否机密',
width: 100,
data: 'isOpen',
type: 'checkbox'
}, {
title: '是否垫付',
width: 100,
data: 'isAdvancedPay',
type: 'checkbox'
}, {
title: '是否禁开发票',
width: 120,
data: 'isInvoice',
type: 'checkbox'
}, {
title: 'FRT',
width: 120,
data: 'feeFrt',
type: 'dropdown',
source: ['PP', 'CC']
}, {
title: '录入人',
width: 100,
data: 'submitBy',
readOnly: true
}, {
title: '录入日期',
width: 100,
data: 'createTime',
readOnly: true
}, {
title: '结算金额',
width: 100,
data: 'settlementAmount',
readOnly: true
}, {
title: '已开票金额',
width: 100,
data: 'invoiceAmount',
readOnly: true
}, {
title: '对账编号',
width: 100,
data: 'debitNo',
readOnly: true
}, {
title: '修改人',
width: 100,
data: 'submitBy',
readOnly: true
}, {
title: '修改日期',
width: 100,
data: 'createTime',
readOnly: true
}, {
title: '发票申请金额',
width: 100,
data: 'orderInvoiceAmount',
readOnly: true
}, {
title: '未开票金额',
width: 100,
data: 'debitNo',
readOnly: true
}, {
title: '审核人',
width: 100,
data: 'auditOperator',
readOnly: true
}, {
title: '审核日期',
width: 100,
data: 'auditDate',
readOnly: true
}
]
//
@ -248,16 +357,25 @@
})
if (item) dict = item[0]
list.value[res[0]]['feeEnName'] = dict['enName']
list.value[res[0]]['currency'] = dict['defaultCurrency']
list.value[res[0]]['unitText'] = dict['defaultUnitName']
list.value[res[0]]['unit'] = dict['defaultUnit']
list.value[res[0]]['customerTypeText'] = dict['defaultDebitName']
list.value[res[0]]['customerType'] = dict['defaultDebit']
list.value[res[0]]['isOpen'] = dict['isOpen']
list.value[res[0]]['isAdvancedPay'] = dict['isAdvancedPay']
list.value[res[0]]['isInvoice'] = dict['isInvoice']
list.value[res[0]]['feeFrt'] = dict['feeFrt']
list.value[res[0]]['feeCode'] = dict['code']
list.value[res[0]]['taxRate'] = dict['taxRate']
}
})
//
if (changes[0][1] === 'feeEnName') {
}
//
if (changes[0][1] === 'customerTypeName') {
if (changes[0][1] === 'customerTypeText') {
getDictOption('djy_cust_prop').then(res => {
const item = res.filter(item => {
return item.name === changes[0][3]
@ -267,7 +385,7 @@
})
}
//
if (changes[0][1] === 'unitName') {
if (changes[0][1] === 'unitText') {
const item = unitDict.value.filter(item => {
return item.name === changes[0][3]
})
@ -319,6 +437,93 @@
}
}
}
//
const save = () => {
const postData = {
BusinessId: '1780891904372772864',
items: list.value.filter(res => {
return res.feeStatus == 1
})
}
loading.value = true
SubmitFee(postData).then(res => {
loading.value = false
createMessage.success(res.message)
}).catch(() => {
loading.value = false
})
}
//
const cancelEdit = () => {
const copy = JSON.parse(JSON.stringify(oldData))
list.value = copy
hotTb.value.hotInstance.loadData(copy)
}
//
const deleteRow = async () => {
const ids = []
list.value.forEach(item => {
if (item.selected && item.id) ids.push(item.id)
})
if (ids.length) {
loading.value = true
const data = await DeleteFee({ ids })
loading.value = false
createMessage.success(data.message)
}
const res = list.value.filter(item => {
return !item.selected
})
list.value = res
hotTb.value.hotInstance.loadData(res)
}
//
const init = () => {
loading.value = true
const postData = {
pageCondition: {
pageIndex: 1,
pageSize: 1000,
sortConditions: []
},
queryCondition: JSON.stringify([
{ FieldName: 'BusinessId', FieldValue: '1780891904372772864', ConditionalType: 1 },
{ FieldName: 'FeeType', FieldValue: props.tbType == 'receive' ? 1 : 2, ConditionalType: 1 }
])
}
GetList(postData).then(res => {
loading.value = false
const { data } = res
data.forEach((item, index) => {
item['feeStatusText'] = feeStatusList[item.feeStatus]
if (item.createTime) item.createTime = item.createTime.split(' ')[0]
if (item.auditDate) item.auditDate = item.auditDate.split(' ')[0]
})
list.value = data
// 使
oldData = JSON.parse(JSON.stringify(data))
if (data.length != 0) {
hotTb.value.hotInstance.updateSettings({
cells: function(row, col) {
//
const props = { readOnly: true }
if (data[row]?.feeStatus != 1 && col != 0) {
//
if (data[row]?.feeStatus == 0) {
props['className'] = 'hot-green'
}
return props
} else {
return
}
}
})
}
hotTb.value.hotInstance.loadData(data)
}).catch(() => {
loading.value = false
})
}
onMounted(() => {
const hot = hotTb.value.hotInstance
//
@ -334,6 +539,8 @@
}
}
})
//
init()
})
watchEffect(() => {
//
@ -384,6 +591,9 @@
background-color: #ffffff;
padding: 0;
margin-top: 10px;
.hot-green {
background: #9ACD32;
}
}
.handsontableInput {
line-height: 30px;

@ -8,8 +8,10 @@
<!-- 主单信息组件 -->
<MainInfo :data="data"></MainInfo>
<!-- 应收表格 -->
<ReceiveTable></ReceiveTable>
<div></div>
<FeeTable tbType="receive"></FeeTable>
<!-- 应付表格 -->
<FeeTable tbType="pay"></FeeTable>
<FeeStatistic />
</div>
</template>
<script lang="ts" setup>
@ -17,7 +19,9 @@
//
import MainInfo from './mainInfo.vue'
//
import ReceiveTable from './receiveTable.vue'
import FeeTable from './feeTable.vue'
//
import FeeStatistic from './feeStatistic.vue'
const props = defineProps({
details: { type: [Object, Array] }
})
@ -39,4 +43,3 @@
// }
// })
</script>

@ -9,10 +9,10 @@ import { BasicColumn, FormSchema } from '/@/components/Table'
import { getDictOption } from '/@/utils/dictUtil'
// 下拉框数据接口
import { GetFeeCurrencySelectList } from '/@/api/common'
// 引入计费标准字典
import { feeUnitDict } from '/@/hooks/dict/index'
// 客户类别下拉框数据
const customTypeDict = ref([])
// 计费标准下拉框数据
const feeUnitDict = ref([])
// 付费方式字典
const payMethodsDict = ref([])
// 费用分组字典
@ -21,6 +21,12 @@ const reconciliationCategorygDict = ref([])
const costGroupingDict = ref([])
// 发票商品名称字典
const productNameDict = ref([])
// 自定义绑定参数
export const dsParams = {
defaultCreditName: '',
defaultDebitName: '',
defaultUnitName: ''
}
export const columns: BasicColumn[] = [
{
title: '费用代码',
@ -214,18 +220,16 @@ export const formSchema: FormSchema[] = [
field: 'defaultUnit',
label: '默认计费标准',
defaultValue: '',
component: 'Select',
component: 'ApiSelect',
colProps: { span: 8 },
componentProps: () => {
getDictOption('fee_unit').then(data => {
feeUnitDict.value = data
})
return {
options: feeUnitDict.value,
allowClear: true,
showSearch: true,
filterOption: (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
api: feeUnitDict,
labelField: 'name',
valueField: 'value',
resultField: 'data',
onChange: (v, obj) => {
dsParams.defaultUnitName = obj?.label || ''
}
}
}
@ -245,6 +249,9 @@ export const formSchema: FormSchema[] = [
showSearch: true,
filterOption: (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
onChange: (v, obj) => {
dsParams.defaultDebitName = obj?.label || ''
}
}
}
@ -264,6 +271,9 @@ export const formSchema: FormSchema[] = [
showSearch: true,
filterOption: (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
onChange: (v, obj) => {
dsParams.defaultCreditName = obj?.label || ''
}
}
}

@ -64,7 +64,7 @@
import { BasicModal, useModalInner } from '/@/components/Modal'
import { BasicForm, useForm } from '/@/components/Form/index'
//
import { formSchema } from '../columns'
import { formSchema, dsParams } from '../columns'
//
import { EditFeeCode, GetFeeCodeInfo } from '../api'
//
@ -128,7 +128,7 @@
const values = await validate()
loading.value = true
setModalProps({ confirmLoading: true, loading: true })
const res: API.DataResult = await EditFeeCode(Object.assign(values, formData))
const res: API.DataResult = await EditFeeCode(Object.assign(values, formData, dsParams))
loading.value = false
if (res.succeeded) {
createMessage.success(res.message)

@ -21,7 +21,11 @@
</template>
</template>
</BasicTable>
<ReceiveTable/>
<!-- 应收表格 -->
<FeeTable tbType="receive"></FeeTable>
<!-- 应付表格 -->
<FeeTable tbType="pay"></FeeTable>
<FeeStatistic></FeeStatistic>
<Modal @register="registerModal" @success="handleSuccess" />
</div>
</template>
@ -34,7 +38,8 @@
import { columns, searchFormSchema } from './columns'
const [registerModal, { openModal }] = useModal()
//
import ReceiveTable from '/@/components/CostEntry/receiveTable.vue'
import FeeTable from '/@/components/CostEntry/feeTable.vue'
import FeeStatistic from '/@/components/CostEntry/feeStatistic.vue'
const [registerTable, { reload, getForm, getPaginationRef }] = useTable({
title: '币别设置',
// api: getSysDictTypeList,

@ -224,11 +224,23 @@
} else {
const types = await getDictOption('djy_cust_prop')
const dict = types.filter(item => {
console.log(item)
return item.name
return rowData.customerTypeName == item.name
})
if (dict && dict.length) {
GetClientListByCode({ code: dict[0].value }).then(res => {
const { data } = res
data.forEach(item => {
item['label'] = item.shortName
item['value'] = item.codeName
})
companyDict.value = data
const results = data.map(item => {
return item.label
})
process(results)
})
}
}
// process(dict)
}
}],
licenseKey: 'non-commercial-and-evaluation',

Loading…
Cancel
Save