fix: 协议子资源单事务保存,并补齐样本编码/OA/质检等近期能力
锁定缴费校验失败时整单回滚,避免样本半成功落库;前端禁止删除锁定缴费样本,改码走更正编码。同步落地样本更正、采集微信通知、OA提及未读、细胞产品套餐与相关迁移文档。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
19bc66ba45
commit
5dad1bb0e3
142 files changed
+95584
-65717
No files matched your search
@@ -166,12 +166,18 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="样本编码">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
样本编码
|
||||
<span v-if="isPersistedSampleCodeLocked" class="form-label-tip"
|
||||
>已落库不可改,请用客户服务「更正编码」</span
|
||||
>
|
||||
</template>
|
||||
<el-input
|
||||
v-if="autoGenerateSampleCode"
|
||||
v-if="autoGenerateSampleCode || isPersistedSampleCodeLocked"
|
||||
:model-value="sampleDraft.sampleCode"
|
||||
disabled
|
||||
placeholder="系统自动生成"
|
||||
:placeholder="autoGenerateSampleCode ? '系统自动生成' : '—'"
|
||||
/>
|
||||
<el-select
|
||||
v-else
|
||||
@@ -381,6 +387,7 @@
|
||||
} from '@/components/business/freight-unlink-dialog/index.vue'
|
||||
import { sampleStatusLabel, sampleStatusTagType } from '@/utils/status-tag-registry'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { samplePaymentHasLockedTransfer } from '@/utils/protocol-sample-payment'
|
||||
import {
|
||||
buildFreightHeaderPayload,
|
||||
freightAmountText as formatFreightAmountText,
|
||||
@@ -424,6 +431,8 @@
|
||||
siteOptions?: Api.Site.SiteItem[]
|
||||
expressCompanyOptions?: Api.ExpressCompany.ExpressCompanyItem[]
|
||||
showPrice?: boolean
|
||||
/** 按样本缴费方案(用于判断锁定转账,禁止删除对应样本) */
|
||||
samplePayments?: Array<Record<string, any>>
|
||||
/** 科研捐赠等:列表与样本弹窗均不展示存储年限及相关采集明细 */
|
||||
hideCollectDetailFields?: boolean
|
||||
/** 添加/编辑样本弹窗内隐藏采集量、单位(不影响列表列) */
|
||||
@@ -440,6 +449,7 @@
|
||||
{
|
||||
protocolId: '',
|
||||
showPrice: false,
|
||||
samplePayments: () => [],
|
||||
hideCollectDetailFields: false,
|
||||
hideDialogCollectAmountUnit: false,
|
||||
autoGenerateSampleCode: false,
|
||||
@@ -495,6 +505,12 @@
|
||||
|
||||
const canUnlinkFreight = computed(() => selectedSamples.value.some((r) => sampleHasFreight(r)))
|
||||
|
||||
/** 已落库样本(有持久化 id)编辑时锁定编码,纠错走客户服务「更正编码」 */
|
||||
const isPersistedSampleCodeLocked = computed(
|
||||
() =>
|
||||
sampleDialogMode.value === 'edit' && Boolean(String(sampleDraft.id || '').trim())
|
||||
)
|
||||
|
||||
function fmtMoney(v: unknown): string {
|
||||
if (v == null || v === '') return '—'
|
||||
return '¥' + formatMoneyDisplay(v)
|
||||
@@ -738,7 +754,7 @@
|
||||
}
|
||||
row.storageYears = st.storageYears
|
||||
}
|
||||
if (props.autoGenerateSampleCode) return
|
||||
if (props.autoGenerateSampleCode || isPersistedSampleCodeLocked.value) return
|
||||
row.sampleCodeId = ''
|
||||
row.sampleCode = ''
|
||||
emit('search-sample-code', '', sampleCodeFilterForRow(row))
|
||||
@@ -1061,6 +1077,7 @@
|
||||
props.samples.length
|
||||
)
|
||||
)
|
||||
sampleDraft.id = ''
|
||||
if (props.autoGenerateSampleCode && props.reserveSampleCode) {
|
||||
try {
|
||||
sampleDraft.sampleCode = await props.reserveSampleCode()
|
||||
@@ -1137,7 +1154,27 @@
|
||||
sampleDialogVisible.value = false
|
||||
}
|
||||
|
||||
function sampleHasLockedPayment(row: Record<string, any>): boolean {
|
||||
const code = String(row?.sampleCode || '').trim()
|
||||
const sampleId = String(row?.id || row?.protocolSampleId || '').trim()
|
||||
return (props.samplePayments || []).some((pay) => {
|
||||
if (!samplePaymentHasLockedTransfer(pay)) return false
|
||||
const payCode = String(pay.sampleCode || '').trim()
|
||||
const paySampleId = String(pay.protocolSampleId || '').trim()
|
||||
if (code && payCode && code === payCode) return true
|
||||
if (sampleId && paySampleId && sampleId === paySampleId) return true
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
function removeSample(index: number) {
|
||||
const row = props.samples[index]
|
||||
if (row && sampleHasLockedPayment(row)) {
|
||||
ElMessage.warning(
|
||||
'该样本存在锁定缴费方案,不可删除;请使用客户服务「更正编码」调整样本编码'
|
||||
)
|
||||
return
|
||||
}
|
||||
const next = [...props.samples]
|
||||
next.splice(index, 1)
|
||||
updateSamples(next)
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
<div class="val">{{ identity.labFlowCode || '—' }}</div>
|
||||
</div>
|
||||
<div class="ctx-item">
|
||||
<label>协议编码</label>
|
||||
<div class="val">{{ identity.protocolCode || '—' }}</div>
|
||||
<label>样本编码</label>
|
||||
<div class="val">{{ identity.sampleCode || '—' }}</div>
|
||||
</div>
|
||||
<div class="ctx-item">
|
||||
<label>母亲姓名</label>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<el-empty
|
||||
v-if="mode === 'view' && !filledFlag && !loading"
|
||||
description="质检尚未填写"
|
||||
description="尚未填写"
|
||||
/>
|
||||
|
||||
<template v-else-if="typeKey && groups.length">
|
||||
@@ -147,7 +147,7 @@
|
||||
const form = reactive<Record<string, string>>({})
|
||||
const identity = reactive({
|
||||
labFlowCode: '',
|
||||
protocolCode: '',
|
||||
sampleCode: '',
|
||||
motherName: '',
|
||||
sampleTypeName: ''
|
||||
})
|
||||
@@ -176,8 +176,8 @@
|
||||
}
|
||||
|
||||
function applyDetail(detail: SampleStorageCertQcDetail) {
|
||||
identity.labFlowCode = detail.labFlowCode || detail.sampleCode || ''
|
||||
identity.protocolCode = detail.protocolCode || ''
|
||||
identity.labFlowCode = detail.labFlowCode || ''
|
||||
identity.sampleCode = detail.sampleCode || props.sampleCode || ''
|
||||
identity.motherName = detail.motherName || ''
|
||||
identity.sampleTypeName = detail.sampleTypeName || props.sampleTypeName || ''
|
||||
filledFlag.value = !!detail.filledFlag
|
||||
@@ -199,7 +199,8 @@
|
||||
typeKey.value = resolveStorageCertQcTypeKey(props.sampleTypeName)
|
||||
filledFlag.value = false
|
||||
resetFormKeys(typeKey.value, {})
|
||||
identity.labFlowCode = code
|
||||
identity.labFlowCode = ''
|
||||
identity.sampleCode = code
|
||||
identity.sampleTypeName = props.sampleTypeName || ''
|
||||
} finally {
|
||||
loading.value = false
|
||||
|
||||
Reference in new issue
Block a user