Batch.vue 10.7 KB
<template>
  <div class="batch-page">
    <div class="page-header">
      <h1>批量订单管理</h1>
      <el-button type="primary" @click="showCreateDialog = true">+ 批量登记</el-button>
    </div>

    <div class="filter-bar">
      <el-input v-model="searchText" placeholder="搜索批次名称..." clearable style="width:240px" />
      <el-select v-model="statusFilter" clearable placeholder="状态筛选" style="width:130px">
        <el-option label="草稿" value="draft" />
        <el-option label="已提交" value="submitted" />
        <el-option label="已审核" value="reviewed" />
      </el-select>
    </div>

    <el-table :data="filteredBatch" border row-key="id" style="width:100%">
      <el-table-column type="selection" width="45" />
      <el-table-column label="批次名称" prop="name" min-width="200">
        <template #default="{ row }">
          <span class="batch-name">{{ row.name }}</span>
        </template>
      </el-table-column>
      <el-table-column label="检测项目" width="100">
        <template #default="{ row }">
          <el-tag size="small" effect="light">{{ row.productType }}</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="送检机构" prop="hospital" min-width="180" show-overflow-tooltip />
      <el-table-column label="区域" prop="region" width="80" />
      <el-table-column label="创建日期" prop="createDate" width="110" />
      <el-table-column label="申请单数" prop="orderCount" width="90" align="center" />
      <el-table-column label="状态" width="100">
        <template #default="{ row }">
          <el-tag :type="statusType(row.status)" size="small" effect="light">{{ statusLabel(row.status) }}</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="200">
        <template #default="{ row }">
          <el-button size="small" text type="primary" @click="viewDetail(row)">详情</el-button>
          <el-button v-if="row.status !== 'reviewed'" size="small" text type="success" @click="reviewBatch(row)">审核</el-button>
          <el-button size="small" text @click="exportBatch(row)">导出</el-button>
          <el-button size="small" text type="danger" @click="deleteBatch(row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 创建批量订单弹窗 -->
    <el-dialog v-model="showCreateDialog" title="批量登记申请单" width="800px" destroy-on-close>
      <el-form :model="createForm" label-width="100px" size="small">
        <el-row :gutter="16">
          <el-col :span="12">
            <el-form-item label="检测项目" :rules="[{required:true}]">
              <el-select v-model="createForm.productType" style="width:100%">
                <el-option label="PGT-M" value="PGT-M" />
                <el-option label="PGT-A" value="PGT-A" />
                <el-option label="ECS" value="ECS" />
                <el-option label="WES" value="WES" />
                <el-option label="AZF" value="AZF" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="批次名称" :rules="[{required:true}]">
              <el-input v-model="createForm.name" placeholder="如:协和医院-PGT-A-04月批次" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="送检机构">
              <el-input v-model="createForm.hospital" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="区域">
              <el-select v-model="createForm.region" style="width:100%">
                <el-option label="华北" value="华北" />
                <el-option label="华东" value="华东" />
                <el-option label="华南" value="华南" />
                <el-option label="华西" value="华西" />
                <el-option label="华中" value="华中" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>

      <!-- 患者列表 -->
      <div class="patient-list-header">
        <span class="pl-title">患者信息列表({{ createForm.patients.length }}人)</span>
        <el-button size="small" @click="addPatientRow">+ 添加患者</el-button>
      </div>
      <el-table :data="createForm.patients" border size="small">
        <el-table-column label="#" type="index" width="40" />
        <el-table-column label="患者姓名">
          <template #default="{ row }">
            <el-input v-model="row.name" placeholder="姓名" size="small" />
          </template>
        </el-table-column>
        <el-table-column label="年龄" width="80">
          <template #default="{ row }">
            <el-input-number v-model="row.age" :min="18" :max="60" size="small" style="width:70px" />
          </template>
        </el-table-column>
        <el-table-column label="样本条码">
          <template #default="{ row }">
            <el-input v-model="row.barcode" placeholder="条码" size="small" />
          </template>
        </el-table-column>
        <el-table-column label="送检日期" width="140">
          <template #default="{ row }">
            <el-date-picker v-model="row.date" type="date" value-format="YYYY-MM-DD" size="small" style="width:130px" />
          </template>
        </el-table-column>
        <el-table-column label="删除" width="60">
          <template #default="{ $index }">
            <el-button size="small" type="danger" plain circle @click="createForm.patients.splice($index, 1)">×</el-button>
          </template>
        </el-table-column>
      </el-table>

      <template #footer>
        <el-button @click="showCreateDialog = false">取消</el-button>
        <el-button @click="saveBatchDraft">保存草稿</el-button>
        <el-button type="primary" @click="submitBatch">提交批次</el-button>
      </template>
    </el-dialog>

    <!-- 详情弹窗 -->
    <el-dialog v-model="showDetailDialog" :title="'批次详情:' + (selectedBatch?.name || '')" width="700px">
      <div v-if="selectedBatch">
        <el-descriptions :column="3" border size="small">
          <el-descriptions-item label="批次名称">{{ selectedBatch.name }}</el-descriptions-item>
          <el-descriptions-item label="检测项目">{{ selectedBatch.productType }}</el-descriptions-item>
          <el-descriptions-item label="状态">
            <el-tag :type="statusType(selectedBatch.status)" size="small">{{ statusLabel(selectedBatch.status) }}</el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="送检机构">{{ selectedBatch.hospital }}</el-descriptions-item>
          <el-descriptions-item label="区域">{{ selectedBatch.region }}</el-descriptions-item>
          <el-descriptions-item label="申请单数">{{ selectedBatch.orderCount }} 个</el-descriptions-item>
          <el-descriptions-item label="创建日期">{{ selectedBatch.createDate }}</el-descriptions-item>
        </el-descriptions>
        <div style="margin-top:16px; font-size:13px; color:#888">批次内申请单已关联,可在申请单管理中查看详情。</div>
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { usePGXStore, type BatchOrder, type ProductType } from '@/stores/pgx'
import { ElMessage, ElMessageBox } from 'element-plus'

const pgxStore = usePGXStore()

const searchText = ref('')
const statusFilter = ref('')
const showCreateDialog = ref(false)
const showDetailDialog = ref(false)
const selectedBatch = ref<BatchOrder | null>(null)

const filteredBatch = computed(() => {
  let list = pgxStore.batchOrders
  if (searchText.value) {
    list = list.filter(b => b.name.toLowerCase().includes(searchText.value.toLowerCase()))
  }
  if (statusFilter.value) {
    list = list.filter(b => b.status === statusFilter.value)
  }
  return list
})

function statusLabel(s: string) {
  return { draft: '草稿', submitted: '已提交', reviewed: '已审核' }[s] || s
}
function statusType(s: string): any {
  return { draft: 'info', submitted: 'warning', reviewed: 'success' }[s] || ''
}

const createForm = ref({
  productType: 'PGT-A' as ProductType,
  name: '',
  hospital: '',
  region: '华北',
  patients: [{ name: '', age: 30, barcode: '', date: new Date().toISOString().split('T')[0] }],
})

function addPatientRow() {
  createForm.value.patients.push({ name: '', age: 30, barcode: '', date: new Date().toISOString().split('T')[0] })
}

function saveBatchDraft() {
  pgxStore.batchOrders.unshift({
    id: 'bt' + Date.now(),
    name: createForm.value.name || '未命名批次',
    productType: createForm.value.productType,
    hospital: createForm.value.hospital,
    region: createForm.value.region,
    createDate: new Date().toISOString().split('T')[0],
    status: 'draft',
    orderCount: createForm.value.patients.filter(p => p.name).length,
  })
  showCreateDialog.value = false
  ElMessage.success('草稿已保存')
}

function submitBatch() {
  pgxStore.batchOrders.unshift({
    id: 'bt' + Date.now(),
    name: createForm.value.name || '未命名批次',
    productType: createForm.value.productType,
    hospital: createForm.value.hospital,
    region: createForm.value.region,
    createDate: new Date().toISOString().split('T')[0],
    status: 'submitted',
    orderCount: createForm.value.patients.filter(p => p.name).length,
  })
  showCreateDialog.value = false
  ElMessage.success('批次已提交')
}

function viewDetail(row: BatchOrder) {
  selectedBatch.value = row
  showDetailDialog.value = true
}

async function reviewBatch(row: BatchOrder) {
  await ElMessageBox.confirm(`确认审核通过批次「${row.name}」?`, '审核确认')
  const idx = pgxStore.batchOrders.findIndex(b => b.id === row.id)
  if (idx !== -1) pgxStore.batchOrders[idx].status = 'reviewed'
  ElMessage.success('审核通过')
}

function exportBatch(row: BatchOrder) {
  ElMessage.success(`批次「${row.name}」数据导出成功`)
}

async function deleteBatch(row: BatchOrder) {
  await ElMessageBox.confirm(`确定删除批次「${row.name}」?`, '删除确认', { type: 'warning' })
  const idx = pgxStore.batchOrders.findIndex(b => b.id === row.id)
  if (idx !== -1) pgxStore.batchOrders.splice(idx, 1)
  ElMessage.success('已删除')
}
</script>

<style scoped>
.batch-page { padding: 24px; }
.page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; }
.page-header h1 { font-size: 20px; font-weight: 600; margin: 0; color: #1a1f2e; }
.filter-bar { display: flex; gap: 12px; margin-bottom: 16px; }
.batch-name { font-weight: 500; color: #1a1f2e; }
.patient-list-header { display: flex; align-items: center; justify-content: space-between; margin: 12px 0 8px; }
.pl-title { font-size: 14px; font-weight: 500; color: #334155; }
</style>