提交
This commit is contained in:
1 parent
a1c2511eb7
commit
eaf7d490ec
382 files changed
+44146
-34245
No files matched your search
@@ -7,20 +7,36 @@
|
||||
<dt>申请单号</dt>
|
||||
<dd>{{ dash(detail?.applicationNo) }}</dd>
|
||||
</div>
|
||||
<div class="view-kv">
|
||||
<dt>患者姓名</dt>
|
||||
<dd>{{ dash(detail?.patientName) }}</dd>
|
||||
</div>
|
||||
<div class="view-kv">
|
||||
<dt>渠道名称</dt>
|
||||
<dd>{{ dash(detail?.channelName) }}</dd>
|
||||
</div>
|
||||
<template v-if="isBasicExperiment">
|
||||
<div class="view-kv">
|
||||
<dt>申请单名称</dt>
|
||||
<dd>{{ dash(detail?.applicationName) }}</dd>
|
||||
</div>
|
||||
<div class="view-kv">
|
||||
<dt>课题名称</dt>
|
||||
<dd>{{ dash(detail?.projectTitle) }}</dd>
|
||||
</div>
|
||||
<div class="view-kv">
|
||||
<dt>客户名称</dt>
|
||||
<dd>{{ dash(detail?.customerName) }}</dd>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="view-kv">
|
||||
<dt>患者姓名</dt>
|
||||
<dd>{{ dash(detail?.patientName) }}</dd>
|
||||
</div>
|
||||
<div class="view-kv">
|
||||
<dt>渠道名称</dt>
|
||||
<dd>{{ dash(detail?.channelName) }}</dd>
|
||||
</div>
|
||||
</template>
|
||||
<div class="view-kv">
|
||||
<dt>细胞类型/规格</dt>
|
||||
<dd>{{ dash(detail?.cellTypeSpec) }}</dd>
|
||||
</div>
|
||||
<div class="view-kv">
|
||||
<dt>输注时间</dt>
|
||||
<dt>{{ pickupTimeLabel }}</dt>
|
||||
<dd>{{ dash(detail?.infusionTime) }}</dd>
|
||||
</div>
|
||||
<div v-if="showPayStatus" class="view-kv">
|
||||
@@ -85,6 +101,7 @@
|
||||
import ProtocolAttachmentReadonly from '@/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { payStatusLabel, payStatusTagType } from '@/utils/status-tag-registry'
|
||||
import { getPilotBizConfig } from '@/config/pilot-biz-type'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -125,6 +142,14 @@
|
||||
.filter(Boolean) as Array<Record<string, unknown>>
|
||||
}
|
||||
|
||||
const isBasicExperiment = computed(
|
||||
() => String(props.detail?.bizType || '') === 'basic_experiment'
|
||||
)
|
||||
|
||||
const pickupTimeLabel = computed(() =>
|
||||
getPilotBizConfig(String(props.detail?.bizType || '')).pickupTimeLabel
|
||||
)
|
||||
|
||||
const receiptAttachmentFiles = computed(() => urlsToFiles(props.detail?.transferAttachments))
|
||||
|
||||
const refundAttachmentFiles = computed(() => urlsToFiles(props.detail?.refundAttachments))
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
width="1200px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
modal-class="protocol-dialog-mask"
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
append-to-body
|
||||
:model-value="modelValue"
|
||||
title="查看商品"
|
||||
width="900px"
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
fetchQcTaskMaterialUsages,
|
||||
type QcTaskMaterialUsageItem
|
||||
} from '@/api/qc-task-result'
|
||||
import { SHOW_QC_TASK_MATERIAL_USAGE } from '@/utils/qc-material-ui'
|
||||
import {
|
||||
taskTypeLabel,
|
||||
taskStatusLabel,
|
||||
@@ -172,7 +173,7 @@
|
||||
;(detail as QcTaskDetail).results = []
|
||||
}
|
||||
viewMaterialUsages.value = []
|
||||
if (detail.taskType !== 'dsfjc') {
|
||||
if (SHOW_QC_TASK_MATERIAL_USAGE && detail.taskType !== 'dsfjc') {
|
||||
try {
|
||||
const savedUsages = await fetchQcTaskMaterialUsages(id).catch(
|
||||
() => [] as QcTaskMaterialUsageItem[]
|
||||
@@ -337,7 +338,7 @@
|
||||
: [{ title: '检测参数', fields: [] }])
|
||||
]
|
||||
const tailSections: ViewSection[] = []
|
||||
if (d.taskType !== 'dsfjc') {
|
||||
if (SHOW_QC_TASK_MATERIAL_USAGE && d.taskType !== 'dsfjc') {
|
||||
tailSections.push({ title: '物料使用', fields: [], slot: 'material-usages' })
|
||||
}
|
||||
if (d.attachmentFiles?.length) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<!-- 样本查看 — 质检信息卡片(协议单/捐赠单查看弹窗复用) -->
|
||||
<template>
|
||||
<div class="sample-card__section sample-card__section--qc">
|
||||
<div class="sample-card__section-label">质检信息</div>
|
||||
<div
|
||||
class="sample-card__section sample-card__section--qc"
|
||||
:class="{ 'sample-card__section--embedded': embedded }"
|
||||
>
|
||||
<div v-if="!embedded" class="sample-card__section-label">{{ sectionLabel || '质检信息' }}</div>
|
||||
|
||||
<div v-if="!qcInfo" class="sample-qc-empty">{{ emptyHint }}</div>
|
||||
|
||||
@@ -128,6 +131,11 @@
|
||||
workOrderStatusLabel?: string
|
||||
qcBizType?: string
|
||||
qcBizTypeLabel?: string
|
||||
triggerSource?: string
|
||||
triggerSourceLabel?: string
|
||||
cellProductCode?: string
|
||||
cellProductName?: string
|
||||
productionWorkOrderNo?: string
|
||||
configuredTime?: string
|
||||
hasLab?: boolean
|
||||
hasThirdParty?: boolean
|
||||
@@ -145,12 +153,20 @@
|
||||
acceptConclusion?: string
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
qcInfo?: SampleQcInfo | null
|
||||
sampleReception?: SampleReceptionBrief | null
|
||||
/** 任务详情弹窗不展示样本信息、样本接收(出库放行等场景) */
|
||||
hideSampleContext?: boolean
|
||||
}>()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
qcInfo?: SampleQcInfo | null
|
||||
sampleReception?: SampleReceptionBrief | null
|
||||
/** 任务详情弹窗不展示样本信息、样本接收(出库放行等场景) */
|
||||
hideSampleContext?: boolean
|
||||
/** 嵌入分组面板时隐藏区块标题与顶部分隔 */
|
||||
embedded?: boolean
|
||||
sectionLabel?: string
|
||||
}>(),
|
||||
{
|
||||
embedded: false
|
||||
}
|
||||
)
|
||||
|
||||
const taskViewVisible = ref(false)
|
||||
const taskViewId = ref('')
|
||||
@@ -181,6 +197,12 @@
|
||||
margin-top: 4px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px dashed #e2e8f0;
|
||||
|
||||
&.sample-card__section--embedded {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sample-card__section-label {
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
<!-- 样本查看 — 质检信息分组(门禁 + 生产过程,入库放行/协议/捐赠查看复用) -->
|
||||
<template>
|
||||
<div
|
||||
class="sample-qc-grouped-panel-root"
|
||||
:class="{ 'sample-qc-grouped-panel-root--section': showSectionLabel }"
|
||||
>
|
||||
<div v-if="showSectionLabel" class="sample-card__section-label">质检信息</div>
|
||||
<div class="sample-qc-grouped-panel">
|
||||
<section class="sample-qc-grouped-panel__block">
|
||||
<h4 class="sample-qc-grouped-panel__subtitle">样本接收质检(入库门禁)</h4>
|
||||
<SampleQcInfoCards
|
||||
embedded
|
||||
:qc-info="gateQcInfo"
|
||||
:sample-reception="sampleReception"
|
||||
:hide-sample-context="hideSampleContext"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section class="sample-qc-grouped-panel__block">
|
||||
<h4 class="sample-qc-grouped-panel__subtitle">生产过程质检</h4>
|
||||
<p class="sample-qc-grouped-panel__hint">过程参考,不参与入库放行判定</p>
|
||||
|
||||
<div v-if="!processQcInfoList?.length" class="sample-qc-grouped-panel__empty">
|
||||
暂无生产过程质检工单
|
||||
</div>
|
||||
|
||||
<div v-else class="sample-qc-grouped-panel__process-list">
|
||||
<article
|
||||
v-for="(item, idx) in processQcInfoList"
|
||||
:key="item.workOrderNo || String(idx)"
|
||||
class="sample-qc-grouped-panel__process-item"
|
||||
>
|
||||
<div class="process-qc-meta">
|
||||
<el-tag type="success" size="small" class="process-qc-meta__tag">生产过程</el-tag>
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">质检对象</span>
|
||||
<span class="qc-kv__value">{{ formatQcTarget(item) }}</span>
|
||||
</div>
|
||||
<div v-if="item.productionWorkOrderNo" class="qc-kv">
|
||||
<span class="qc-kv__label">生产工单</span>
|
||||
<span class="qc-kv__value qc-kv__value--code">{{
|
||||
item.productionWorkOrderNo
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<SampleQcInfoCards embedded :qc-info="item" :hide-sample-context="hideSampleContext" />
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SampleQcInfoCards, {
|
||||
type SampleQcInfo,
|
||||
type SampleReceptionBrief
|
||||
} from '@/components/business/sample-qc-info-cards/index.vue'
|
||||
|
||||
defineOptions({ name: 'SampleQcInfoGroupedPanel' })
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
gateQcInfo?: SampleQcInfo | null
|
||||
processQcInfoList?: SampleQcInfo[]
|
||||
sampleReception?: SampleReceptionBrief | null
|
||||
hideSampleContext?: boolean
|
||||
/** 协议/捐赠样本卡内展示外层「质检信息」标题 */
|
||||
showSectionLabel?: boolean
|
||||
}>(),
|
||||
{
|
||||
processQcInfoList: () => [],
|
||||
showSectionLabel: true
|
||||
}
|
||||
)
|
||||
|
||||
function formatQcTarget(item: SampleQcInfo) {
|
||||
const parts = [item.cellProductCode, item.cellProductName].filter(Boolean)
|
||||
return parts.length ? parts.join(' · ') : '—'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sample-qc-grouped-panel-root--section {
|
||||
margin-top: 4px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px dashed #e2e8f0;
|
||||
}
|
||||
|
||||
.sample-card__section-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #94a3b8;
|
||||
margin-bottom: 10px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.sample-qc-grouped-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.sample-qc-grouped-panel__block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sample-qc-grouped-panel__subtitle {
|
||||
margin: 0;
|
||||
padding-left: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
line-height: 1.4;
|
||||
border-left: 3px solid #10b981;
|
||||
}
|
||||
|
||||
.sample-qc-grouped-panel__hint {
|
||||
margin: 0;
|
||||
padding-left: 13px;
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.sample-qc-grouped-panel__empty {
|
||||
padding: 4px 0 4px 13px;
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.sample-qc-grouped-panel__process-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding-left: 13px;
|
||||
}
|
||||
|
||||
.sample-qc-grouped-panel__process-item {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.process-qc-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
gap: 8px 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.process-qc-meta__tag {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.process-qc-meta .qc-kv {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.qc-kv {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.qc-kv__label {
|
||||
color: #94a3b8;
|
||||
flex-shrink: 0;
|
||||
min-width: 56px;
|
||||
}
|
||||
|
||||
.qc-kv__value {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
word-break: break-word;
|
||||
|
||||
&--code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -127,6 +127,7 @@
|
||||
width="840px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
modal-class="protocol-dialog-mask"
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
@@ -265,6 +266,7 @@
|
||||
width="1200px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
modal-class="protocol-dialog-mask"
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
width="560px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
append-to-body
|
||||
modal-class="protocol-dialog-mask"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
width="480px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
append-to-body
|
||||
modal-class="protocol-dialog-mask"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
width="560px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
append-to-body
|
||||
modal-class="protocol-dialog-mask"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
|
||||
Reference in new issue
Block a user