优化
This commit is contained in:
@@ -9,24 +9,24 @@ import { useAuthStore } from '@/core/auth/auth'
|
||||
import { ApiResponseError } from '@/shared/api/http'
|
||||
import {
|
||||
adjustInboundFeeDetail, createInboundFeeDetail, deleteInboundFeeDetail,
|
||||
listInboundFeeDetails, listInboundFeeItemOptions, listInboundFeePartnerOptions,
|
||||
listInboundFeeDetails, listInboundFeeItemOptions, listInboundFeeSupplierOptions,
|
||||
reviewInboundFeeDetail, updateInboundFeeDetail, voidInboundFeeDetail,
|
||||
} from '@/modules/unified-orders/api/inboundFeeDetails'
|
||||
import InboundFeeDetailManager from './InboundFeeDetailManager.vue'
|
||||
import type { InboundFeeDetail } from '@/modules/unified-orders/api/inboundFeeDetails'
|
||||
|
||||
vi.mock('@/modules/unified-orders/api/inboundFeeDetails', () => ({
|
||||
listInboundFeeDetails: vi.fn(), listInboundFeeItemOptions: vi.fn(), listInboundFeePartnerOptions: vi.fn(),
|
||||
listInboundFeeDetails: vi.fn(), listInboundFeeItemOptions: vi.fn(), listInboundFeeSupplierOptions: vi.fn(),
|
||||
createInboundFeeDetail: vi.fn(), updateInboundFeeDetail: vi.fn(), deleteInboundFeeDetail: vi.fn(),
|
||||
reviewInboundFeeDetail: vi.fn(), adjustInboundFeeDetail: vi.fn(), voidInboundFeeDetail: vi.fn(),
|
||||
}))
|
||||
|
||||
const pending: InboundFeeDetail = {
|
||||
id: '11', inboundId: '88', feeItemId: '31', sourceType: 'MANUAL', sourceFeeVersion: 1,
|
||||
inboundCode: 'RK-88', direction: 'RECEIVABLE', partnerId: null, partnerName: null,
|
||||
inboundCode: 'RK-88', direction: 'RECEIVABLE', supplierId: null, supplierName: null, supplierCategory: null,
|
||||
businessDate: '2026-08-12', customerName: '测试客户', feeTypeNameSnapshot: '操作费',
|
||||
unitPrice: 10, currency: 'CNY', amount: 100, remark: 'marker', reviewStatus: 'PENDING',
|
||||
reviewReason: null, reviewerName: null, reviewedAt: null, publicationStatus: 'DRAFT', settlementIncluded: true,
|
||||
reviewReason: null, reviewerName: null, reviewedAt: null, publicationStatus: 'DRAFT',
|
||||
}
|
||||
const approved: InboundFeeDetail = { ...pending, id: '12', sourceFeeVersion: 3, reviewStatus: 'APPROVED', publicationStatus: 'PUBLISHED' }
|
||||
const source = readFileSync(join(process.cwd(), 'src/modules/unified-orders/components/InboundFeeDetailManager.vue'), 'utf8')
|
||||
@@ -44,8 +44,8 @@ describe('InboundFeeDetailManager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks(); document.body.innerHTML = ''
|
||||
vi.mocked(listInboundFeeDetails).mockResolvedValue([pending, approved])
|
||||
vi.mocked(listInboundFeeItemOptions).mockResolvedValue([{ id: '31', feeTypeName: '操作费', warehouseReceivable: true, warehousePayable: true }])
|
||||
vi.mocked(listInboundFeePartnerOptions).mockResolvedValue([{ id: '41', partnerName: '测试往来单位' }])
|
||||
vi.mocked(listInboundFeeItemOptions).mockResolvedValue([{ id: '31', feeTypeName: '操作费', feeCode: 'OPERATION', supplierCategory: 'TRUCKING', warehouseReceivable: true, warehousePayable: true }])
|
||||
vi.mocked(listInboundFeeSupplierOptions).mockResolvedValue([{ id: '41', supplierName: '测试供应商', category: 'TRUCKING' }])
|
||||
vi.mocked(createInboundFeeDetail).mockResolvedValue(pending)
|
||||
vi.mocked(updateInboundFeeDetail).mockResolvedValue(pending)
|
||||
vi.mocked(adjustInboundFeeDetail).mockResolvedValue({ ...approved, reviewStatus: 'PENDING' })
|
||||
@@ -144,14 +144,14 @@ describe('InboundFeeDetailManager', () => {
|
||||
expect(source).toContain('@keyframes fee-refresh-spin')
|
||||
})
|
||||
|
||||
it('loads RECEIVABLE fee items without requesting counterparties', async () => {
|
||||
it('loads RECEIVABLE fee items without requesting suppliers', async () => {
|
||||
const wrapper = mountManager(['firstleg:inbound:fee-detail:view', 'firstleg:inbound:fee-detail:edit', 'firstleg:fee-item:view'])
|
||||
await flushPromises()
|
||||
await wrapper.findAll('button').find((button) => button.text().includes('新增费用'))?.trigger('click')
|
||||
await flushPromises()
|
||||
expect(listInboundFeeItemOptions).toHaveBeenCalledTimes(1)
|
||||
expect(listInboundFeePartnerOptions).not.toHaveBeenCalled()
|
||||
expect(document.body.textContent).toContain('缺少往来单位查看权限')
|
||||
expect(listInboundFeeSupplierOptions).not.toHaveBeenCalled()
|
||||
expect(document.body.textContent).toContain('缺少供应商查看权限')
|
||||
expect(document.body.textContent).toContain('应收费用不受影响')
|
||||
})
|
||||
|
||||
@@ -162,25 +162,41 @@ describe('InboundFeeDetailManager', () => {
|
||||
expect(listInboundFeeItemOptions).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps fee items usable when the independent counterparty request fails', async () => {
|
||||
vi.mocked(listInboundFeeDetails).mockResolvedValue([{ ...pending, direction: 'PAYABLE', partnerId: '41', partnerName: '测试往来单位' }])
|
||||
vi.mocked(listInboundFeePartnerOptions).mockRejectedValue(new ApiResponseError('往来单位不可用', 'A0403', 403, null, 'request-partner', 'trace-partner'))
|
||||
it('keeps fee items usable but blocks payable save when the supplier request fails', async () => {
|
||||
vi.mocked(listInboundFeeDetails).mockResolvedValue([{ ...pending, direction: 'PAYABLE', supplierId: '41', supplierName: '测试供应商', supplierCategory: 'TRUCKING' }])
|
||||
vi.mocked(listInboundFeeSupplierOptions).mockRejectedValue(new ApiResponseError('供应商不可用', 'A0403', 403, null, 'request-supplier', 'trace-supplier'))
|
||||
const wrapper = mountManager([
|
||||
'firstleg:inbound:fee-detail:view', 'firstleg:inbound:fee-detail:edit',
|
||||
'firstleg:fee-item:view', 'platform:counterparty:list',
|
||||
'firstleg:fee-item:view', 'platform:supplier:list',
|
||||
])
|
||||
await flushPromises()
|
||||
await wrapper.findAll('button').find((button) => button.text().includes('编辑'))?.trigger('click')
|
||||
await flushPromises()
|
||||
expect(listInboundFeeItemOptions).toHaveBeenCalledTimes(1)
|
||||
expect(listInboundFeePartnerOptions).toHaveBeenCalledTimes(1)
|
||||
expect(listInboundFeeSupplierOptions).toHaveBeenCalledWith('TRUCKING')
|
||||
expect(document.body.textContent).toContain('当前账号无权访问该数据')
|
||||
expect(document.body.textContent).toContain('A0403')
|
||||
expect(document.body.textContent).toContain('request-partner')
|
||||
expect(document.body.textContent).toContain('trace-partner')
|
||||
expect(document.body.textContent).toContain('request-supplier')
|
||||
expect(document.body.textContent).toContain('trace-supplier')
|
||||
expect(document.querySelector('.fee-form .el-select')?.classList.contains('is-disabled')).toBe(false)
|
||||
})
|
||||
|
||||
it('fails closed when a payable fee item has no supplier category', async () => {
|
||||
vi.mocked(listInboundFeeItemOptions).mockResolvedValue([{ id: '31', feeTypeName: '未映射费用', feeCode: 'UNMAPPED', supplierCategory: null, warehouseReceivable: false, warehousePayable: true }])
|
||||
const wrapper = mountManager([
|
||||
'firstleg:inbound:fee-detail:view', 'firstleg:inbound:fee-detail:edit',
|
||||
'firstleg:fee-item:view', 'platform:supplier:list',
|
||||
])
|
||||
await flushPromises()
|
||||
await wrapper.findAll('button').find((button) => button.text().includes('新增费用'))?.trigger('click')
|
||||
await flushPromises()
|
||||
const state = (wrapper.vm.$ as unknown as { setupState: { save(): Promise<void>; form: { direction: string; feeItemId: string; supplierId: string | null; amount: number } } }).setupState
|
||||
state.form.direction = 'PAYABLE'; state.form.feeItemId = '31'; state.form.supplierId = '41'; state.form.amount = 100; await flushPromises()
|
||||
await state.save(); await flushPromises()
|
||||
expect(createInboundFeeDetail).not.toHaveBeenCalled()
|
||||
expect(document.body.textContent).toContain('该应付费用项目未配置供应商类别,已阻断保存')
|
||||
})
|
||||
|
||||
it('refreshes fee rows and the order detail after every successful write', () => {
|
||||
expect(source).toContain("async function finishAction(message: string) {\n await load(); emit('changed')")
|
||||
expect(source).toMatch(/createInboundFeeDetail[\s\S]*updateInboundFeeDetail[\s\S]*adjustInboundFeeDetail[\s\S]*await finishAction\(/)
|
||||
|
||||
@@ -10,12 +10,12 @@ import StatusBadge from '@/shared/components/StatusBadge.vue'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import {
|
||||
adjustInboundFeeDetail, createInboundFeeDetail, deleteInboundFeeDetail,
|
||||
listInboundFeeDetails, listInboundFeeItemOptions, listInboundFeePartnerOptions,
|
||||
listInboundFeeDetails, listInboundFeeItemOptions, listInboundFeeSupplierOptions,
|
||||
reviewInboundFeeDetail, updateInboundFeeDetail, voidInboundFeeDetail,
|
||||
} from '@/modules/unified-orders/api/inboundFeeDetails'
|
||||
import type {
|
||||
InboundFeeDetail, InboundFeeItemOption,
|
||||
InboundFeePartnerOption, SaveInboundFeeDetailRequest,
|
||||
InboundFeeSupplierOption, SaveInboundFeeDetailRequest,
|
||||
} from '@/modules/unified-orders/api/inboundFeeDetails'
|
||||
|
||||
const props = defineProps<{ accountId: string; inboundId: string }>()
|
||||
@@ -24,12 +24,12 @@ const emit = defineEmits<{ changed: []; summary: [value: OtherFeeSummary] }>()
|
||||
const access = useAccessPreviewStore()
|
||||
const rows = ref<InboundFeeDetail[]>([])
|
||||
const feeItems = ref<InboundFeeItemOption[]>([])
|
||||
const partners = ref<InboundFeePartnerOption[]>([])
|
||||
const suppliers = ref<InboundFeeSupplierOption[]>([])
|
||||
const feeItemsLoading = ref(false)
|
||||
const partnersLoading = ref(false)
|
||||
const suppliersLoading = ref(false)
|
||||
const loadedSupplierCategory = ref<string | null>(null)
|
||||
const feeItemsError = ref<ApiErrorDetails | null>(null)
|
||||
const partnersError = ref<ApiErrorDetails | null>(null)
|
||||
const partnersLoadAttempted = ref(false)
|
||||
const suppliersError = ref<ApiErrorDetails | null>(null)
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const loadError = ref<ApiErrorDetails | null>(null)
|
||||
@@ -40,11 +40,12 @@ const target = ref<InboundFeeDetail | null>(null)
|
||||
const formRef = ref<FormInstance>()
|
||||
const rejectOpen = ref(false)
|
||||
const rejectReason = ref('')
|
||||
let supplierLoadSequence = 0
|
||||
|
||||
const emptyForm = (): SaveInboundFeeDetailRequest => ({
|
||||
feeItemId: '', direction: 'RECEIVABLE', businessDate: new Date().toISOString().slice(0, 10),
|
||||
unitPrice: 0, currency: 'CNY', amount: 0, settlementIncluded: true,
|
||||
partnerId: null, partnerName: null, remark: null,
|
||||
unitPrice: 0, currency: 'CNY', amount: 0,
|
||||
supplierId: null, remark: null,
|
||||
})
|
||||
const form = reactive<SaveInboundFeeDetailRequest>(emptyForm())
|
||||
const rules: FormRules = {
|
||||
@@ -60,12 +61,13 @@ const normalizedInboundId = computed(() => {
|
||||
return identifier && identifier !== 'null' && identifier !== 'undefined' ? identifier : ''
|
||||
})
|
||||
const canViewFeeItems = computed(() => access.can('firstleg:fee-item:view'))
|
||||
const canViewPartners = computed(() => access.can('platform:counterparty:list'))
|
||||
const canViewSuppliers = computed(() => access.can('platform:supplier:list'))
|
||||
const directionOptions = computed(() => [
|
||||
{ label: '应收', value: 'RECEIVABLE' },
|
||||
{ label: '应付', value: 'PAYABLE', disabled: !canViewPartners.value },
|
||||
{ label: '应付', value: 'PAYABLE', disabled: !canViewSuppliers.value },
|
||||
])
|
||||
const applicableItems = computed(() => feeItems.value.filter((item) => form.direction === 'RECEIVABLE' ? item.warehouseReceivable : item.warehousePayable))
|
||||
const selectedFeeItem = computed(() => feeItems.value.find((item) => String(item.id) === String(form.feeItemId)))
|
||||
|
||||
function isManual(row: InboundFeeDetail) { return row.sourceType === 'MANUAL' }
|
||||
function canEdit(row: InboundFeeDetail) { return isManual(row) && access.can('firstleg:inbound:fee-detail:edit') && row.reviewStatus === 'PENDING' }
|
||||
@@ -84,7 +86,7 @@ async function load() {
|
||||
loading.value = true; loadError.value = null
|
||||
try {
|
||||
rows.value = await listInboundFeeDetails(props.accountId, normalizedInboundId.value)
|
||||
const approved = rows.value.filter((row) => isManual(row) && row.reviewStatus === 'APPROVED' && row.publicationStatus !== 'VOIDED' && row.settlementIncluded)
|
||||
const approved = rows.value.filter((row) => isManual(row) && row.reviewStatus === 'APPROVED' && row.publicationStatus !== 'VOIDED')
|
||||
emit('summary', {
|
||||
currency: approved.find((row) => row.currency)?.currency || 'CNY',
|
||||
approvedReceivable: approved.filter((row) => row.direction === 'RECEIVABLE').reduce((sum, row) => sum + Number(row.amount || 0), 0),
|
||||
@@ -103,37 +105,42 @@ async function loadFeeItems() {
|
||||
finally { feeItemsLoading.value = false }
|
||||
}
|
||||
|
||||
async function loadPartners() {
|
||||
if (!canViewPartners.value || partnersLoading.value || partners.value.length || partnersLoadAttempted.value) return
|
||||
partnersLoadAttempted.value = true
|
||||
partnersLoading.value = true; partnersError.value = null
|
||||
try { partners.value = await listInboundFeePartnerOptions() }
|
||||
catch (error) { partnersError.value = getApiErrorDetails(error, '往来单位加载失败') }
|
||||
finally { partnersLoading.value = false }
|
||||
async function loadSuppliers() {
|
||||
const sequence = ++supplierLoadSequence
|
||||
const item = selectedFeeItem.value
|
||||
suppliers.value = []; loadedSupplierCategory.value = null; suppliersError.value = null
|
||||
if (!canViewSuppliers.value || form.direction !== 'PAYABLE' || !item?.supplierCategory) return
|
||||
const requestedCategory = item.supplierCategory
|
||||
suppliersLoading.value = true
|
||||
try {
|
||||
const result = await listInboundFeeSupplierOptions(requestedCategory)
|
||||
if (sequence === supplierLoadSequence && form.direction === 'PAYABLE' && selectedFeeItem.value?.supplierCategory === requestedCategory) {
|
||||
suppliers.value = result
|
||||
loadedSupplierCategory.value = requestedCategory
|
||||
}
|
||||
}
|
||||
catch (error) { if (sequence === supplierLoadSequence) suppliersError.value = getApiErrorDetails(error, '供应商加载失败') }
|
||||
finally { if (sequence === supplierLoadSequence) suppliersLoading.value = false }
|
||||
}
|
||||
|
||||
function assignForm(row?: InboundFeeDetail) {
|
||||
Object.assign(form, row ? {
|
||||
feeItemId: row.feeItemId, direction: row.direction, businessDate: row.businessDate,
|
||||
unitPrice: Number(row.unitPrice), currency: row.currency, amount: Number(row.amount),
|
||||
settlementIncluded: Boolean(row.settlementIncluded), partnerId: row.partnerId,
|
||||
partnerName: row.partnerName, remark: row.remark,
|
||||
supplierId: row.supplierId, remark: row.remark,
|
||||
} : emptyForm())
|
||||
}
|
||||
|
||||
async function openEditor(mode: 'create' | 'edit' | 'adjust', row?: InboundFeeDetail) {
|
||||
partnersLoadAttempted.value = false
|
||||
editorMode.value = mode; target.value = row || null; assignForm(row); actionError.value = null
|
||||
feeItemsError.value = null; partnersError.value = null
|
||||
feeItemsError.value = null; suppliersError.value = null
|
||||
editorOpen.value = true
|
||||
if (!feeItems.value.length) await loadFeeItems()
|
||||
if (form.direction === 'PAYABLE') await loadPartners()
|
||||
if (form.direction === 'PAYABLE') await loadSuppliers()
|
||||
}
|
||||
|
||||
function normalizePayload(): SaveInboundFeeDetailRequest {
|
||||
const partner = partners.value.find((item) => String(item.id) === String(form.partnerId))
|
||||
return { ...form, currency: 'CNY', partnerId: form.direction === 'PAYABLE' ? form.partnerId : null,
|
||||
partnerName: form.direction === 'PAYABLE' ? partner?.partnerName || form.partnerName : null,
|
||||
return { ...form, currency: 'CNY', supplierId: form.direction === 'PAYABLE' ? form.supplierId : null,
|
||||
remark: form.remark?.trim() || null }
|
||||
}
|
||||
|
||||
@@ -144,8 +151,14 @@ async function finishAction(message: string) {
|
||||
async function save() {
|
||||
if (!await formRef.value?.validate()) return
|
||||
if (!canViewFeeItems.value) { actionError.value = { message: '当前账号缺少费用项目查看权限,无法保存费用' }; return }
|
||||
if (form.direction === 'PAYABLE' && !canViewPartners.value) { actionError.value = { message: '当前账号缺少往来单位查看权限,无法维护应付费用' }; return }
|
||||
if (form.direction === 'PAYABLE' && !form.partnerId) { actionError.value = { message: '应付费用必须选择往来单位' }; return }
|
||||
const selectedItem = feeItems.value.find((item) => String(item.id) === String(form.feeItemId))
|
||||
if (form.direction === 'PAYABLE' && !canViewSuppliers.value) { actionError.value = { message: '当前账号缺少供应商查看权限,无法维护应付费用' }; return }
|
||||
if (form.direction === 'PAYABLE' && !selectedItem?.supplierCategory) { actionError.value = { message: '该应付费用项目未配置供应商类别,已阻断保存' }; return }
|
||||
if (form.direction === 'PAYABLE' && suppliersError.value) { actionError.value = { message: '供应商列表加载失败,无法校验应付费用供应商' }; return }
|
||||
if (form.direction === 'PAYABLE' && loadedSupplierCategory.value !== selectedItem?.supplierCategory) { actionError.value = { message: '供应商候选尚未按费用项目类别加载完成,无法保存' }; return }
|
||||
if (form.direction === 'PAYABLE' && !form.supplierId) { actionError.value = { message: '应付费用必须选择供应商' }; return }
|
||||
const selectedSupplier = suppliers.value.find((supplier) => supplier.id === String(form.supplierId))
|
||||
if (form.direction === 'PAYABLE' && (!selectedSupplier || selectedSupplier.category !== selectedItem?.supplierCategory)) { actionError.value = { message: '所选供应商与费用项目要求的供应商类别不匹配' }; return }
|
||||
saving.value = true; actionError.value = null
|
||||
try {
|
||||
const payload = normalizePayload()
|
||||
@@ -188,9 +201,10 @@ async function voidRow(row: InboundFeeDetail) {
|
||||
|
||||
watch(() => [props.accountId, normalizedInboundId.value], () => void load())
|
||||
watch(() => form.direction, (direction) => {
|
||||
if (direction === 'RECEIVABLE') { form.partnerId = null; form.partnerName = null; partnersError.value = null }
|
||||
else void loadPartners()
|
||||
if (direction === 'RECEIVABLE') { supplierLoadSequence += 1; form.supplierId = null; suppliers.value = []; loadedSupplierCategory.value = null; suppliersError.value = null; suppliersLoading.value = false }
|
||||
else void loadSuppliers()
|
||||
})
|
||||
watch(() => form.feeItemId, () => { form.supplierId = null; suppliers.value = []; loadedSupplierCategory.value = null; suppliersError.value = null; if (form.direction === 'PAYABLE') void loadSuppliers() })
|
||||
onMounted(() => void load())
|
||||
</script>
|
||||
|
||||
@@ -228,12 +242,11 @@ onMounted(() => void load())
|
||||
<el-table-column label="来源" width="120"><template #default="{ row }">{{ row.sourceType === 'FBA_PRICING' ? '头程派生' : '人工录入' }}</template></el-table-column>
|
||||
<el-table-column label="应收/应付" width="110"><template #default="{ row }">{{ directionLabel(row.direction) }}</template></el-table-column>
|
||||
<el-table-column prop="feeTypeNameSnapshot" label="费用类型" min-width="150" />
|
||||
<el-table-column label="往来单位/客户" min-width="170"><template #default="{ row }">{{ row.direction === 'PAYABLE' ? row.partnerName : row.customerName || '—' }}</template></el-table-column>
|
||||
<el-table-column label="供应商/客户" min-width="170"><template #default="{ row }">{{ row.direction === 'PAYABLE' ? row.supplierName : row.customerName || '—' }}</template></el-table-column>
|
||||
<el-table-column prop="businessDate" label="业务发生日" width="130" />
|
||||
<el-table-column prop="currency" label="货币" width="90" />
|
||||
<el-table-column label="金额" min-width="120"><template #default="{ row }">{{ Number(row.amount).toFixed(2) }}</template></el-table-column>
|
||||
<el-table-column label="单价" min-width="110"><template #default="{ row }">{{ Number(row.unitPrice).toFixed(4) }}</template></el-table-column>
|
||||
<el-table-column label="参与结算" width="110"><template #default="{ row }">{{ row.settlementIncluded ? '是' : '否' }}</template></el-table-column>
|
||||
<el-table-column prop="sourceFeeVersion" label="版本" width="85" />
|
||||
<el-table-column prop="publicationStatus" label="发布状态" min-width="110" />
|
||||
<el-table-column prop="reviewReason" label="驳回原因" min-width="180" show-overflow-tooltip />
|
||||
@@ -245,19 +258,18 @@ onMounted(() => void load())
|
||||
<AppDialog v-model="editorOpen" :title="editorMode === 'create' ? '新增费用' : editorMode === 'adjust' ? '调整已审核费用' : '编辑费用'" width="min(680px, calc(100vw - 28px))" :confirm-loading="saving" confirm-text="保存" @confirm="save">
|
||||
<el-alert v-if="editorMode === 'adjust'" title="调整将生成新版本并重新进入待审核状态" type="warning" :closable="false" show-icon />
|
||||
<el-alert v-if="!canViewFeeItems" title="当前账号缺少费用项目查看权限,无法新增或修改费用" type="warning" :closable="false" show-icon />
|
||||
<el-alert v-if="!canViewPartners" title="当前账号缺少往来单位查看权限,应付费用不可维护;应收费用不受影响" type="warning" :closable="false" show-icon />
|
||||
<el-alert v-if="!canViewSuppliers" title="当前账号缺少供应商查看权限,应付费用不可维护;应收费用不受影响" type="warning" :closable="false" show-icon />
|
||||
<el-alert v-if="feeItemsError" type="error" :closable="true" @close="feeItemsError = null"><template #title>{{ feeItemsError.message }}</template><ApiErrorMeta :details="feeItemsError" /></el-alert>
|
||||
<el-alert v-if="partnersError" type="error" :closable="true" @close="partnersError = null"><template #title>{{ partnersError.message }}</template><ApiErrorMeta :details="partnersError" /></el-alert>
|
||||
<el-alert v-if="suppliersError" type="error" :closable="true" @close="suppliersError = null"><template #title>{{ suppliersError.message }}</template><ApiErrorMeta :details="suppliersError" /></el-alert>
|
||||
<el-alert v-if="actionError" type="error" :closable="true" @close="actionError = null"><template #title>{{ actionError.message }}</template><ApiErrorMeta :details="actionError" /></el-alert>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" class="fee-form">
|
||||
<el-form-item label="应收/应付" prop="direction"><el-segmented v-model="form.direction" :options="directionOptions" /></el-form-item>
|
||||
<el-form-item label="费用项目" prop="feeItemId"><el-select v-model="form.feeItemId" filterable :loading="feeItemsLoading" :disabled="!canViewFeeItems"><el-option v-for="item in applicableItems" :key="item.id" :label="item.feeTypeName" :value="item.id" /></el-select></el-form-item>
|
||||
<el-form-item v-if="form.direction === 'PAYABLE'" label="往来单位" required><el-select v-model="form.partnerId" filterable :loading="partnersLoading" :disabled="!canViewPartners"><el-option v-for="partner in partners" :key="partner.id" :label="partner.partnerName" :value="partner.id" /></el-select></el-form-item>
|
||||
<el-form-item v-if="form.direction === 'PAYABLE'" label="供应商" required><el-select v-model="form.supplierId" filterable :loading="suppliersLoading" :disabled="!canViewSuppliers || !selectedFeeItem?.supplierCategory || !!suppliersError" placeholder="请先选择已配置供应商类别的费用项目"><el-option v-for="supplier in suppliers" :key="supplier.id" :label="supplier.supplierName" :value="supplier.id" /></el-select></el-form-item>
|
||||
<el-form-item label="业务发生日" prop="businessDate"><el-date-picker v-model="form.businessDate" type="date" value-format="YYYY-MM-DD" /></el-form-item>
|
||||
<el-form-item label="货币" prop="currency"><el-input model-value="CNY" readonly aria-label="货币 CNY" /></el-form-item>
|
||||
<el-form-item label="单价" prop="unitPrice"><el-input-number v-model="form.unitPrice" :min="0" :precision="4" /></el-form-item>
|
||||
<el-form-item label="金额" prop="amount"><el-input-number v-model="form.amount" :min="0" :precision="2" /></el-form-item>
|
||||
<el-form-item label="参与结算"><el-switch v-model="form.settlementIncluded" /></el-form-item>
|
||||
<el-form-item label="备注"><el-input v-model="form.remark" type="textarea" :rows="3" maxlength="500" show-word-limit /></el-form-item>
|
||||
</el-form>
|
||||
</AppDialog>
|
||||
|
||||
Reference in New Issue
Block a user