Files
zkxw/web-admin/src/views/cell-storage/sample-info/index.vue
T
2026-06-15 11:43:16 +08:00

242 lines
5.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 样本信息管理(样本采集 / 样本接收 / 样本质检 / 样本入库) -->
<template>
<div class="page-container">
<section class="panel">
<nav class="pill-tab-nav" role="tablist" aria-label="视图类型">
<div class="pill-tab-nav__rail">
<div class="pill-tab-nav__progress" :style="pillProgressStyle" />
<button
v-for="(item, index) in entityTabs"
:key="item.key"
type="button"
class="pill-tab-nav__step"
:class="{
'is-active': activeTab === item.key,
'is-done': index < activeTabIndex
}"
role="tab"
:aria-selected="activeTab === item.key"
@click="activeTab = item.key"
>
<span class="pill-tab-nav__index">{{ index + 1 }}</span>
<span class="pill-tab-nav__text">
<span class="pill-tab-nav__title">{{ item.title }}</span>
<span class="pill-tab-nav__desc">{{ item.desc }}</span>
</span>
</button>
</div>
</nav>
<SampleCollectionInfoTab v-if="activeTab === 'sample-collection'" />
<SampleReceptionInfoTab v-else-if="activeTab === 'sample-reception'" />
<SampleQcInfoTab v-else-if="activeTab === 'sample-qc'" />
<StorageEntryInfoTab v-else-if="activeTab === 'storage-entry'" />
</section>
</div>
</template>
<script setup lang="ts">
import SampleCollectionInfoTab from '../protocol-audit/SampleCollectionInfoTab.vue'
import SampleReceptionInfoTab from '../protocol-audit/SampleReceptionInfoTab.vue'
import SampleQcInfoTab from '../protocol-audit/SampleQcInfoTab.vue'
import StorageEntryInfoTab from '../protocol-audit/StorageEntryInfoTab.vue'
defineOptions({ name: 'CellStorageSampleInfo' })
const VALID_TABS = [
'sample-collection',
'sample-reception',
'sample-qc',
'storage-entry'
] as const
type EntityTabKey = (typeof VALID_TABS)[number]
const entityTabs: Array<{
key: EntityTabKey
title: string
desc: string
}> = [
{
key: 'sample-collection',
title: '样本采集信息',
desc: '协议样本采集进度'
},
{
key: 'sample-reception',
title: '样本接收信息',
desc: '接收结论与短信通知'
},
{
key: 'sample-qc',
title: '样本质检信息',
desc: '质检结论与短信通知'
},
{
key: 'storage-entry',
title: '样本入库信息',
desc: '样本入库记录查询'
}
]
const route = useRoute()
const activeTab = ref<EntityTabKey>('sample-collection')
const activeTabIndex = computed(() =>
Math.max(
0,
entityTabs.findIndex((item) => item.key === activeTab.value)
)
)
const pillProgressStyle = computed(() => ({
width: `calc(${100 / entityTabs.length}% - 3px)`,
transform: `translateX(${activeTabIndex.value * 100}%)`
}))
onMounted(() => {
const tab = String(route.query.tab || '')
if (VALID_TABS.includes(tab as EntityTabKey)) {
activeTab.value = tab as EntityTabKey
}
})
</script>
<style scoped lang="scss">
.pill-tab-nav {
padding: 16px 20px 0;
display: flex;
flex-direction: column;
align-items: center;
}
.pill-tab-nav__rail {
display: flex;
align-items: center;
width: 70%;
max-width: 100%;
margin-inline: auto;
background: var(--surface, #fff);
border: 1px solid var(--border, #e2e8f0);
border-radius: 10px;
padding: 6px;
position: relative;
overflow: hidden;
}
.pill-tab-nav__progress {
position: absolute;
top: 6px;
bottom: 6px;
left: 6px;
background: var(--primary, #10b981);
border: 1px solid var(--primary-dark, #059669);
border-radius: 8px;
box-shadow: 0 1px 3px rgb(16 185 129 / 28%);
transition:
transform 0.25s ease,
width 0.25s ease;
z-index: 0;
}
.pill-tab-nav__step {
flex: 1;
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
min-width: 0;
min-height: 44px;
padding: 8px 10px;
border: none;
background: transparent;
cursor: pointer;
border-radius: 8px;
transition: color 0.2s ease;
}
.pill-tab-nav__index {
width: 22px;
height: 22px;
border-radius: 50%;
background: #e2e8f0;
color: #475569;
font-size: 11px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.2s ease;
}
.pill-tab-nav__text {
display: flex;
flex-direction: column;
align-items: flex-start;
min-width: 0;
text-align: left;
}
.pill-tab-nav__title {
font-size: 13px;
font-weight: 600;
color: var(--mint-text-muted, #64748b);
white-space: nowrap;
}
.pill-tab-nav__desc {
font-size: 11px;
color: #94a3b8;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.pill-tab-nav__step.is-active .pill-tab-nav__index {
background: #fff;
color: var(--primary-dark, #059669);
}
.pill-tab-nav__step.is-active .pill-tab-nav__title,
.pill-tab-nav__step.is-active .pill-tab-nav__desc {
color: #fff;
}
.pill-tab-nav__step.is-done .pill-tab-nav__index {
background: var(--primary-bg, #ecfdf5);
color: var(--primary-dark, #059669);
border: 1px solid var(--mint-border, #c5e0d4);
}
.pill-tab-nav__step.is-done .pill-tab-nav__title {
color: var(--text, #1e293b);
}
@media (width <= 960px) {
.pill-tab-nav__desc {
display: none;
}
.pill-tab-nav__text {
align-items: center;
}
}
@media (width <= 720px) {
.pill-tab-nav {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
align-items: stretch;
}
.pill-tab-nav__rail {
width: 100%;
min-width: 680px;
}
}
</style>