<script lang="ts" setup>
|
import type { InspectionProgressDetail, InspectionProgressRecord, InspectionProjectStage } from '#/api/x/lims/jianyanguanli/jianyan_jindu';
|
|
import { computed, onMounted, ref } from 'vue';
|
|
import {
|
Card as ACard,
|
Collapse as ACollapse,
|
CollapsePanel as ACollapsePanel,
|
Descriptions as ADescriptions,
|
DescriptionsItem as ADescriptionsItem,
|
Empty as AEmpty,
|
Spin as ASpin,
|
Steps as ASteps,
|
Timeline as ATimeline,
|
TimelineItem as ATimelineItem,
|
} from 'ant-design-vue';
|
|
import { postInspectionProgressDetail } from '#/api/x/lims/jianyanguanli/jianyan_jindu';
|
import { onlineUtils } from '#/utils/jnpf';
|
|
defineOptions({ name: 'XlimsJianyanguanliJianyanJindu' });
|
|
const loading = ref(false);
|
const detail = ref<InspectionProgressDetail>();
|
const expandedProjectIds = ref<string[]>([]);
|
|
const progressRecords = computed(() => detail.value?.inspection_progress || []);
|
const projectItems = computed(() => detail.value?.project_detail || []);
|
const hasProgress = computed(() => progressRecords.value.length > 0);
|
|
function getTimelineColor(status: InspectionProgressRecord['status']) {
|
if (status === 'completed') return 'green';
|
if (status === 'current') return 'blue';
|
return 'gray';
|
}
|
|
function getStepItems(stages: InspectionProjectStage[]) {
|
return stages.map((stage) => ({ status: stage.status, title: stage.title }));
|
}
|
|
async function loadDetail() {
|
loading.value = true;
|
try {
|
const qingyandanId = onlineUtils.getViewParam('qingyandan_id');
|
const response = await postInspectionProgressDetail({
|
qingyandan_id: typeof qingyandanId === 'string' ? qingyandanId : undefined,
|
});
|
detail.value = response.data;
|
expandedProjectIds.value = response.data.project_detail.map((item) => item.id);
|
} finally {
|
loading.value = false;
|
}
|
}
|
|
onMounted(loadDetail);
|
</script>
|
|
<template>
|
<div class="jnpf-content-wrapper inspection-page">
|
<div class="jnpf-content-wrapper-center inspection-page__center">
|
<ASpin :spinning="loading" class="inspection-page__spin">
|
<template v-if="detail">
|
<ACard class="sample-card" title="样品信息">
|
<ADescriptions :column="{ xs: 1, sm: 2, xl: 4 }" size="middle">
|
<ADescriptionsItem label="样品名称">{{ detail.sample_info.name }}</ADescriptionsItem>
|
<ADescriptionsItem label="样品编码">{{ detail.sample_info.code }}</ADescriptionsItem>
|
<ADescriptionsItem label="批号">{{ detail.sample_info.batch_no }}</ADescriptionsItem>
|
<ADescriptionsItem label="规格">{{ detail.sample_info.specification }}</ADescriptionsItem>
|
</ADescriptions>
|
</ACard>
|
|
<div class="inspection-content" :class="{ 'without-progress': !hasProgress }">
|
<section v-if="hasProgress" class="progress-panel" aria-label="检验进度">
|
<ATimeline class="progress-timeline">
|
<ATimelineItem v-for="record in progressRecords" :key="record.id" :color="getTimelineColor(record.status)">
|
<ACard :title="record.title" class="progress-card" size="small">
|
<ADescriptions :column="{ xs: 1, sm: 2 }" size="small">
|
<ADescriptionsItem label="操作人">{{ record.operator || '-' }}</ADescriptionsItem>
|
<ADescriptionsItem v-if="record.department" label="部门">{{ record.department }}</ADescriptionsItem>
|
<ADescriptionsItem v-if="record.xiangmu_mingcheng" label="项目名称">
|
{{ record.xiangmu_mingcheng }}
|
</ADescriptionsItem>
|
<ADescriptionsItem label="操作时间">{{ record.operated_at || '-' }}</ADescriptionsItem>
|
</ADescriptions>
|
</ACard>
|
</ATimelineItem>
|
</ATimeline>
|
</section>
|
|
<ACard class="project-panel" title="检验项目详情">
|
<ACollapse v-if="projectItems.length" v-model:active-key="expandedProjectIds" expand-icon-position="end" ghost>
|
<ACollapsePanel v-for="project in projectItems" :key="project.id" :header="project.name">
|
<ASteps :items="getStepItems(project.stages)" class="project-steps" responsive size="small" />
|
<div class="stage-records">
|
<ADescriptions
|
v-for="stage in project.stages.filter((item) => item.operator || item.operated_at)"
|
:key="`${project.id}-${stage.title}`"
|
:column="{ xs: 1, sm: 3 }"
|
class="stage-record"
|
size="small">
|
<ADescriptionsItem label="项目状态">{{ stage.title }}</ADescriptionsItem>
|
<ADescriptionsItem label="操作人">{{ stage.operator || '-' }}</ADescriptionsItem>
|
<ADescriptionsItem label="操作时间">{{ stage.operated_at || '-' }}</ADescriptionsItem>
|
</ADescriptions>
|
</div>
|
</ACollapsePanel>
|
</ACollapse>
|
<AEmpty v-else description="暂无检验项目" />
|
</ACard>
|
</div>
|
</template>
|
</ASpin>
|
</div>
|
</div>
|
</template>
|
|
<style lang="scss" scoped>
|
.inspection-page {
|
min-width: 0;
|
overflow: hidden;
|
background: #f4f6f8;
|
|
&__center {
|
min-height: 0;
|
padding: 12px;
|
overflow: hidden auto !important;
|
background: transparent;
|
}
|
|
&__spin {
|
display: block;
|
min-height: 360px;
|
}
|
}
|
|
.sample-card,
|
.project-panel,
|
.progress-card {
|
border-radius: 6px;
|
box-shadow: 0 3px 14px rgb(31 44 61 / 5%);
|
}
|
|
.sample-card {
|
:deep(.ant-card-head-title) {
|
font-size: 16px;
|
}
|
|
:deep(.ant-descriptions-item-content) {
|
overflow-wrap: anywhere;
|
}
|
}
|
|
.inspection-content {
|
display: grid;
|
grid-template-columns: minmax(420px, 1fr) minmax(520px, 1.06fr);
|
gap: 30px;
|
align-items: start;
|
margin-top: 26px;
|
|
&.without-progress {
|
grid-template-columns: minmax(0, 1fr);
|
}
|
}
|
|
.progress-panel {
|
min-width: 0;
|
}
|
|
.progress-timeline {
|
margin: 8px 0 0 6px;
|
|
:deep(.ant-timeline-item-content) {
|
min-height: 0;
|
margin-inline-start: 26px;
|
}
|
}
|
|
.progress-card {
|
margin-bottom: 20px;
|
}
|
|
.project-panel {
|
min-width: 0;
|
|
:deep(.ant-card-head-title) {
|
font-size: 16px;
|
}
|
|
:deep(.ant-card-body) {
|
padding-top: 0;
|
}
|
|
:deep(.ant-collapse-header-text) {
|
font-weight: 600;
|
}
|
}
|
|
.stage-records {
|
display: grid;
|
gap: 10px;
|
margin-top: 24px;
|
}
|
|
.stage-record {
|
:deep(.ant-descriptions-row > td) {
|
padding-bottom: 6px;
|
}
|
}
|
|
.project-steps {
|
padding: 14px 18px;
|
background: #f5f7fa;
|
border-radius: 6px;
|
}
|
|
@media (max-width: 1100px) {
|
.inspection-content {
|
grid-template-columns: minmax(0, 1fr);
|
}
|
}
|
|
@media (max-width: 700px) {
|
.inspection-page__center {
|
padding: 8px;
|
}
|
|
.inspection-content {
|
gap: 18px;
|
margin-top: 18px;
|
}
|
|
.project-steps {
|
padding: 12px 8px;
|
}
|
}
|
</style>
|