Orders.vue 6.61 KB
<template>
  <div class="orders-page">
    <div class="page-header">
      <h1>申请单管理</h1>
      <div class="header-actions">
        <el-button v-if="pgxStore.currentRole === 'sampler'" type="primary" @click="$router.push('/pgx/register')">+ 新建申请单</el-button>
        <el-button plain @click="handleExport">导出数据</el-button>
      </div>
    </div>

    <!-- 搜索 + 筛选 -->
    <div class="filter-bar">
      <el-input v-model="searchText" placeholder="搜索患者姓名、申请单编号..." clearable style="width:280px" prefix-icon="Search" />
      <el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" style="width:280px" />
    </div>

    <el-tabs v-model="activeTab" class="status-tabs">
      <el-tab-pane label="全部" name="all" />
      <el-tab-pane name="draft">
        <template #label><span>草稿 <el-badge :value="pgxStore.stats.draft" :hidden="!pgxStore.stats.draft" /></span></template>
      </el-tab-pane>
      <el-tab-pane name="submitted">
        <template #label><span>已提交 <el-badge :value="pgxStore.stats.submitted" :hidden="!pgxStore.stats.submitted" type="warning" /></span></template>
      </el-tab-pane>
      <el-tab-pane label="接收审核" name="received" />
      <el-tab-pane label="检测中" name="testing" />
      <el-tab-pane label="已出报告" name="reported" />
      <el-tab-pane name="failed">
        <template #label><span>检测异常 <el-badge :value="pgxStore.stats.failed" :hidden="!pgxStore.stats.failed" type="danger" /></span></template>
      </el-tab-pane>
    </el-tabs>

    <el-table :data="filteredOrders" row-key="id" style="width:100%" @row-click="(row: any) => $router.push('/pgx/orders/' + row.id)">
      <el-table-column type="index" width="50" />
      <el-table-column label="申请单编号" width="160">
        <template #default="{ row }">
          <span class="order-no-link">{{ row.orderNo }}</span>
          <el-tag v-if="row.urgency" type="danger" size="small" effect="plain" style="margin-left:4px">急</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="患者姓名" prop="patientName" width="100" />
      <el-table-column label="检测项目" width="120">
        <template #default="{ row }">
          <el-tag size="small" effect="light">{{ row.productType }}</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="送检单位" prop="hospitalName" min-width="180" show-overflow-tooltip />
      <el-table-column label="提交日期" prop="submitDate" width="110" />
      <el-table-column label="状态" width="110">
        <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="LIMS" width="120">
        <template #default="{ row }">
          <span :style="{ color: limsColor(row.limsStatus), fontSize:'12px' }">
            {{ limsLabel(row.limsStatus) }}
          </span>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="160" @click.stop>
        <template #default="{ row }">
          <el-button size="small" text type="primary" @click.stop="$router.push('/pgx/orders/' + row.id)">详情</el-button>
          <el-button v-if="row.editable" size="small" text type="primary" @click.stop="$router.push('/pgx/orders/' + row.id)">编辑</el-button>
          <el-button size="small" text type="danger" @click.stop="handleDelete(row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <div class="table-footer">
      共 {{ filteredOrders.length }} 条记录
    </div>
  </div>
</template>

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

const pgxStore = usePGXStore()
const router = useRouter()
const route = useRoute()

const searchText = ref('')
const activeTab = ref('all')
const dateRange = ref<[string, string] | null>(null)

// 从路由 query 初始化 tab
onMounted(() => {
  if (route.query.tab) activeTab.value = route.query.tab as string
})

const filteredOrders = computed(() => {
  let list = pgxStore.orders
  if (activeTab.value !== 'all') {
    if (activeTab.value === 'reported') {
      list = list.filter(o => o.status === 'reported' || o.hasReport)
    } else if (activeTab.value === 'failed') {
      list = list.filter(o => o.failed)
    } else {
      list = list.filter(o => o.status === activeTab.value)
    }
  }
  if (searchText.value) {
    const kw = searchText.value.toLowerCase()
    list = list.filter(o =>
      o.patientName.toLowerCase().includes(kw) ||
      o.orderNo.toLowerCase().includes(kw) ||
      o.hospitalName.toLowerCase().includes(kw)
    )
  }
  if (dateRange.value) {
    const [start, end] = dateRange.value
    list = list.filter(o => o.submitDate >= start && o.submitDate <= end)
  }
  return list
})

function statusLabel(s: string) {
  return { draft:'草稿', submitted:'已提交', received:'接收审核', testing:'检测中', reported:'已出报告', failed:'检测异常' }[s] || s
}
function statusType(s: string) {
  return { draft:'info', submitted:'warning', received:'', testing:'primary', reported:'success', failed:'danger' }[s] || ''
}
function limsLabel(s: string) {
  return { pending:'⏳ 待推送', pushed:'✓ 已推送', failed:'⚠ 推送异常' }[s] || s
}
function limsColor(s: string) {
  return { pending:'#aaa', pushed:'#52c41a', failed:'#ff4d4f' }[s] || '#aaa'
}

async function handleDelete(row: Order) {
  await ElMessageBox.confirm(`确定删除申请单 ${row.orderNo} 吗?此操作不可恢复。`, '删除确认', { type: 'warning', confirmButtonText: '确定删除', cancelButtonText: '取消' })
  pgxStore.deleteOrder(row.id)
  ElMessage.success('已删除')
}

function handleExport() {
  ElMessage.success('数据导出成功,文件已保存')
}
</script>

<style scoped>
.orders-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; }
.header-actions { display: flex; gap: 10px; }
.filter-bar { display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.status-tabs { margin-bottom: 4px; }
.order-no-link { color: #409eff; font-size: 13px; cursor: pointer; }
.table-footer { padding: 12px 0; color: #888; font-size: 13px; text-align: right; }
:deep(.el-table__row) { cursor: pointer; }
:deep(.el-table__row:hover td) { background: #f0f7ff !important; }
</style>