feat(mobile-oa-h5): integrate sample payment panel into protocol form

Validate and persist samplePayments on cell_storage protocol save.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-10-13 15:30:22 +08:00
1 parent 3354e66d82
commit a7b8560f5f
1 file changed
+52
@@ -25,6 +25,14 @@
@field-change="handleFieldChange"
/>
<ProtocolSamplePaymentPanel
v-if="context.module === 'protocol' && formData.source === 'cell_storage'"
v-model="formData.samplePayments"
:samples="formData.samples || []"
:disabled="formBusy"
@update:model-value="onSamplePaymentsChange"
/>
<section v-if="context.module === 'freight' && action !== 'edit'" class="market-form-section">
<h2>关联样本</h2>
<van-search
@@ -82,10 +90,18 @@ import {
} from '../../market/action-policy.js'
import {
buildMarketActionPayload,
mergeProtocolSamplePayments,
pickConfiguredPayload,
sanitizeMarketPayload,
validateRequiredFields
} from '../../market/form-payload.js'
import {
protocolTotalMismatchHint,
sampleScheduleDuesMismatchHint,
sampleScheduleRequiredFieldsHint,
sumSampleContractAmounts
} from '../../market/protocol-sample-payment.js'
import ProtocolSamplePaymentPanel from '../../market/components/ProtocolSamplePaymentPanel.vue'
import { appendMarketAttachment } from '../../market/attachment-presenter.js'
import { marketErrorMessage } from '../../market/market-error.js'
import { canLeaveMarketForm, createAsyncGenerationGuard } from '../../market/form-state.js'
@@ -172,6 +188,11 @@ function setSectionValue(section, value) {
dirty.value = true
}
function onSamplePaymentsChange(value) {
formData.value = { ...formData.value, samplePayments: value }
dirty.value = true
}
async function handleFieldChange({ section, field, value, index }) {
if (context.value.module !== 'protocol') return
if (section.key === 'samples' && field.key === 'sampleCodeId') {
@@ -279,6 +300,7 @@ function initialForm(detail) {
customer: {},
collection: {},
samples: [],
samplePayments: [],
payment: {},
attachments: {},
confirm: { confirmed: false }
@@ -293,6 +315,7 @@ function initialForm(detail) {
customer: detail.customer || {},
collection: detail.collection || {},
samples: detail.samples || [],
samplePayments: detail.samplePayments || [],
payment: detail.payment || {},
attachments: detail.attachments || {},
confirm: { confirmed: false }
@@ -418,6 +441,10 @@ function buildPayload() {
? { ...fields, items: selectedItems() }
: fields
}
if (context.value.module === 'protocol') {
const fields = pickConfiguredPayload(visibleSections.value, formData.value)
return mergeProtocolSamplePayments(fields, formData.value.samplePayments)
}
return pickConfiguredPayload(visibleSections.value, formData.value)
}
@@ -428,6 +455,31 @@ async function submit() {
showToast(validationMessage)
return
}
if (context.value.module === 'protocol' && formData.value.source === 'cell_storage') {
for (const pay of formData.value.samplePayments || []) {
const requiredHint = sampleScheduleRequiredFieldsHint(pay)
if (requiredHint) {
showToast(requiredHint)
return
}
const mismatchHint = sampleScheduleDuesMismatchHint(pay)
if (mismatchHint) {
showToast(mismatchHint)
return
}
}
const totalHint = protocolTotalMismatchHint(
formData.value.protocolTotalAmount,
sumSampleContractAmounts(formData.value.samplePayments || [])
)
if (totalHint) {
try {
await showConfirmDialog({ title: '提示', message: totalHint })
} catch {
return
}
}
}
const payload = buildMarketActionPayload(
context.value.module,
action.value,