费用明细可以增加费用
This commit is contained in:
@@ -78,3 +78,29 @@ export function listTransferNoOptions(keyword) {
|
||||
params: { keyword }
|
||||
})
|
||||
}
|
||||
|
||||
/** 计费详情:增加应收费用明细 */
|
||||
export function addIncomeBillFee(data) {
|
||||
return request({
|
||||
url: '/price/billing/bill/fee',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/** 计费详情:编辑手动应收费用 */
|
||||
export function updateIncomeBillFee(data) {
|
||||
return request({
|
||||
url: '/price/billing/bill/fee',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/** 计费详情:删除手动应收费用 */
|
||||
export function deleteIncomeBillFee(feeId) {
|
||||
return request({
|
||||
url: '/price/billing/bill/fee/' + feeId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<bill-detail v-model="open" :title="title" :loading="loading" :detail="detail" />
|
||||
<bill-detail v-model="open" :title="title" :loading="loading" :detail="detail" @updated="onDetailUpdated" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -156,6 +156,11 @@ function handleView(row) {
|
||||
})
|
||||
}
|
||||
|
||||
function onDetailUpdated(data) {
|
||||
detail.value = data
|
||||
tableRef.value?.refresh?.()
|
||||
}
|
||||
|
||||
function formatMoney(val) {
|
||||
if (val === null || val === undefined || val === '') return '-'
|
||||
const num = Number(val)
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
:title="dlgTitle"
|
||||
width="520px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="96px">
|
||||
<el-form-item :label="lblFeeName" prop="scId">
|
||||
<el-select
|
||||
v-model="form.scId"
|
||||
filterable
|
||||
clearable
|
||||
:placeholder="phSurcharge"
|
||||
style="width: 100%;"
|
||||
@change="onSurchargeChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="s in surchargeOptions"
|
||||
:key="s.scId"
|
||||
:label="s.scName"
|
||||
:value="s.scId"
|
||||
>
|
||||
<span>{{ s.scName }}</span>
|
||||
<span style="float: right; color: var(--el-text-color-secondary); font-size: 12px;">{{ s.chargeValue }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="lblFeeType" prop="feeType">
|
||||
<el-select v-model="form.feeType" :placeholder="phFeeType" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="d in (bms_fee_type || [])"
|
||||
:key="d.value"
|
||||
:label="d.label"
|
||||
:value="d.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="lblAmount" prop="amount">
|
||||
<el-input-number
|
||||
v-model="form.amount"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
:step="0.1"
|
||||
controls-position="right"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="lblCurrency" prop="currency">
|
||||
<el-select v-model="form.currency" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="d in (bms_currency || [])"
|
||||
:key="d.value"
|
||||
:label="d.label"
|
||||
:value="d.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="lblRemark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" :placeholder="phRemark" maxlength="200" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="$emit('update:modelValue', false)">{{ btnCancel }}</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="submit">{{ btnOk }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { listSurchargeOptions } from '@/api/price/surcharge'
|
||||
import { addIncomeBillFee, updateIncomeBillFee } from '@/api/price/billing'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
billId: { type: String, default: '' },
|
||||
chargeWeight: { type: [Number, String], default: null },
|
||||
freightAmount: { type: [Number, String], default: null },
|
||||
/** edit row when present */
|
||||
editRow: { type: Object, default: null }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { bms_fee_type, bms_currency } = useDict('bms_fee_type', 'bms_currency')
|
||||
|
||||
const isEdit = computed(() => !!(props.editRow && props.editRow.feeId))
|
||||
const dlgTitle = computed(() => (isEdit.value ? '编辑费用' : '增加费用'))
|
||||
const lblFeeName = '费用名称'
|
||||
const lblFeeType = '费用类型'
|
||||
const lblAmount = '金额'
|
||||
const lblCurrency = '币种'
|
||||
const lblRemark = '说明'
|
||||
const phSurcharge = '请选择附加费'
|
||||
const phFeeType = '请选择费用类型'
|
||||
const phRemark = '选填'
|
||||
const btnCancel = '取消'
|
||||
const btnOk = '保存'
|
||||
|
||||
const formRef = ref()
|
||||
const saving = ref(false)
|
||||
const surchargeOptions = ref([])
|
||||
const form = ref(emptyForm())
|
||||
|
||||
const rules = {
|
||||
scId: [{ required: true, message: '请选择附加费', trigger: 'change' }],
|
||||
feeType: [{ required: true, message: '请选择费用类型', trigger: 'change' }],
|
||||
amount: [{ required: true, message: '请输入金额', trigger: 'blur' }],
|
||||
currency: [{ required: true, message: '请选择币种', trigger: 'change' }]
|
||||
}
|
||||
|
||||
function emptyForm() {
|
||||
return {
|
||||
feeId: undefined,
|
||||
scId: undefined,
|
||||
feeType: 'SURCHARGE',
|
||||
amount: undefined,
|
||||
currency: 'CNY',
|
||||
remark: ''
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, (open) => {
|
||||
if (!open) return
|
||||
loadSurcharges().then(() => {
|
||||
if (isEdit.value) {
|
||||
const row = props.editRow
|
||||
form.value = {
|
||||
feeId: row.feeId,
|
||||
scId: row.scId || undefined,
|
||||
feeType: row.feeType || 'SURCHARGE',
|
||||
amount: row.amount != null ? Number(row.amount) : undefined,
|
||||
currency: displayCurrency(row.currency),
|
||||
remark: row.remark || ''
|
||||
}
|
||||
} else {
|
||||
form.value = emptyForm()
|
||||
}
|
||||
nextTick(() => formRef.value?.clearValidate())
|
||||
})
|
||||
})
|
||||
|
||||
function displayCurrency(v) {
|
||||
if (!v || v === 'RMB' || v === 'rmb') return 'CNY'
|
||||
return v
|
||||
}
|
||||
|
||||
function loadSurcharges() {
|
||||
return listSurchargeOptions({ ssCode: 'on' }).then(res => {
|
||||
surchargeOptions.value = res.data || []
|
||||
})
|
||||
}
|
||||
|
||||
function calcAmount(s) {
|
||||
if (!s || s.chargeValue == null) return 0
|
||||
const cv = Number(s.chargeValue)
|
||||
const weight = Number(props.chargeWeight || 0)
|
||||
const freight = Number(props.freightAmount || 0)
|
||||
const type = String(s.chargeType || 'FIXED').toUpperCase()
|
||||
let amount = cv
|
||||
if (type === 'WEIGHT') amount = cv * weight
|
||||
else if (type === 'RATE') amount = freight * cv
|
||||
return Number(amount.toFixed(4))
|
||||
}
|
||||
|
||||
function onSurchargeChange(scId) {
|
||||
const s = (surchargeOptions.value || []).find(x => String(x.scId) === String(scId))
|
||||
if (!s) return
|
||||
form.value.feeType = s.feeType || 'SURCHARGE'
|
||||
form.value.currency = 'CNY'
|
||||
form.value.amount = calcAmount(s)
|
||||
if (s.scNote && !form.value.remark) {
|
||||
form.value.remark = s.scNote
|
||||
}
|
||||
}
|
||||
|
||||
function submit() {
|
||||
formRef.value?.validate(valid => {
|
||||
if (!valid) return
|
||||
if (!isEdit.value && !props.billId) {
|
||||
proxy.$modal.msgError('计费ID为空')
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
const payload = {
|
||||
scId: form.value.scId,
|
||||
feeType: form.value.feeType,
|
||||
amount: form.value.amount,
|
||||
currency: form.value.currency || 'CNY',
|
||||
remark: form.value.remark
|
||||
}
|
||||
const req = isEdit.value
|
||||
? updateIncomeBillFee({ ...payload, feeId: form.value.feeId })
|
||||
: addIncomeBillFee({ ...payload, billId: props.billId })
|
||||
req.then(res => {
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
emit('update:modelValue', false)
|
||||
emit('success', res.data || null)
|
||||
}).finally(() => {
|
||||
saving.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -90,6 +90,9 @@
|
||||
<div class="sec-title">费用明细</div>
|
||||
<el-tabs v-model="feeTab" class="fee-tabs">
|
||||
<el-tab-pane label="应收" name="income">
|
||||
<div class="fee-toolbar">
|
||||
<el-button type="primary" plain size="small" icon="Plus" v-hasPermi="['price:billing:feeAdd']" @click="openAddFee">增加费用</el-button>
|
||||
</div>
|
||||
<el-table v-if="incomeFees.length" :data="incomeFees" border size="small" class="fee-table">
|
||||
<el-table-column label="费用名称" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ displayFeeName(row) }}</template>
|
||||
@@ -114,6 +117,15 @@
|
||||
<el-table-column label="说明" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.remark || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="canEditManualFee(row)">
|
||||
<el-button link type="primary" v-hasPermi="['price:billing:feeEdit']" @click="openEditFee(row)">编辑</el-button>
|
||||
<el-button link type="danger" v-hasPermi="['price:billing:feeRemove']" @click="handleDeleteFee(row)">删除</el-button>
|
||||
</template>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-else description="暂无应收明细" :image-size="64" />
|
||||
</el-tab-pane>
|
||||
@@ -149,10 +161,23 @@
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<add-income-fee-dialog
|
||||
v-model="addFeeOpen"
|
||||
:bill-id="detail?.billId || ''"
|
||||
:charge-weight="detail?.chargeWeight ?? detail?.weight"
|
||||
:freight-amount="detail?.freightAmount"
|
||||
:edit-row="editFeeRow"
|
||||
@success="onFeeAdded"
|
||||
/>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import AddIncomeFeeDialog from './AddIncomeFeeDialog.vue'
|
||||
import { deleteIncomeBillFee } from '@/api/price/billing'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
title: { type: String, default: '计费详情' },
|
||||
@@ -160,9 +185,11 @@ const props = defineProps({
|
||||
detail: { type: Object, default: null }
|
||||
})
|
||||
|
||||
defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits(['update:modelValue', 'updated'])
|
||||
|
||||
const feeTab = ref('income')
|
||||
const addFeeOpen = ref(false)
|
||||
const editFeeRow = ref(null)
|
||||
const { bms_fee_type, bms_currency } = useDict('bms_fee_type', 'bms_currency')
|
||||
|
||||
const incomeFees = computed(() => {
|
||||
@@ -181,6 +208,37 @@ watch(() => props.detail, () => {
|
||||
feeTab.value = 'income'
|
||||
})
|
||||
|
||||
function canEditManualFee(row) {
|
||||
return row && String(row.manualFlag) === '1' && row.arBillStatus !== 'BILLED'
|
||||
}
|
||||
|
||||
function openAddFee() {
|
||||
if (!props.detail?.billId) return
|
||||
editFeeRow.value = null
|
||||
addFeeOpen.value = true
|
||||
}
|
||||
|
||||
function openEditFee(row) {
|
||||
editFeeRow.value = row
|
||||
addFeeOpen.value = true
|
||||
}
|
||||
|
||||
function handleDeleteFee(row) {
|
||||
if (!canEditManualFee(row)) return
|
||||
proxy.$modal.confirm('确认删除该费用吗?').then(() => {
|
||||
return deleteIncomeBillFee(row.feeId)
|
||||
}).then(res => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
emit('updated', res.data || null)
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function onFeeAdded(data) {
|
||||
editFeeRow.value = null
|
||||
if (data) emit('updated', data)
|
||||
}
|
||||
|
||||
|
||||
function customerActiveLabel(status) {
|
||||
const map = { ACTIVE: '正常', MISSING: '不存在', DISABLED: '停用' }
|
||||
return map[status] || status || '-'
|
||||
@@ -378,6 +436,11 @@ function sourceTagType(source) {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 500;
|
||||
}
|
||||
.fee-toolbar {
|
||||
display: flex;
|
||||
/* justify-content: flex-end; */
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.fee-tabs {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user