yeg
2026-09-09 528d52f2c7c6f911c54cae6e3f25500eab4384fe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
import { useFormationReports, type FormationReportRecord } from '@/stores/formationReports'
import { createFormationReportPrintData, openFormationReportPrint } from '@/utils/formationReportPrint'
 
interface FilterState { reportName: string; creator: string; dateRange: [string, string] | null }
const router = useRouter()
const { records, removeReport } = useFormationReports()
const filters = reactive<FilterState>({ reportName: '', creator: '', dateRange: null })
const appliedFilters = ref<FilterState>({ reportName: '', creator: '', dateRange: null })
const currentPage = ref(1)
const pageSize = ref(10)
const creatorOptions = computed(() => [...new Set(records.value.map(item => item.creator))].sort((a, b) => a.localeCompare(b, 'zh-CN')))
const filteredRecords = computed(() => {
  const query = appliedFilters.value
  const keyword = query.reportName.trim().toLowerCase()
  return records.value.filter(item => !keyword || item.name.toLowerCase().includes(keyword) || String(item.id).includes(keyword))
    .filter(item => !query.creator || query.creator === '全部' || item.creator === query.creator)
    .filter(item => !query.dateRange || (item.createdAt.slice(0, 10) >= query.dateRange[0] && item.createdAt.slice(0, 10) <= query.dateRange[1]))
    .sort((a, b) => b.createdAt.localeCompare(a.createdAt))
})
const pagedRecords = computed(() => filteredRecords.value.slice((currentPage.value - 1) * pageSize.value, currentPage.value * pageSize.value))
function applySearch() { appliedFilters.value = { reportName: filters.reportName, creator: filters.creator, dateRange: filters.dateRange ? [...filters.dateRange] as [string, string] : null }; currentPage.value = 1; ElMessage.success(`已找到 ${filteredRecords.value.length} 份化成报表`) }
function resetSearch() { Object.assign(filters, { reportName: '', creator: '', dateRange: null }); appliedFilters.value = { reportName: '', creator: '', dateRange: null }; currentPage.value = 1 }
function openCreate() { router.push('/report-formation/new') }
function openEdit(item: FormationReportRecord) { router.push(`/report-formation/${item.id}/edit`) }
function openView(item: FormationReportRecord) { router.push({ path: `/report-formation/${item.id}/edit`, query: { mode: 'view' } }) }
function downloadPdf(item: FormationReportRecord) {
  const opened = openFormationReportPrint(createFormationReportPrintData(item))
  if (!opened) { ElMessage.warning('浏览器阻止了报表窗口,请允许弹出窗口后重试'); return }
  ElMessage.info('已打开打印版报表,请在打印窗口选择“另存为 PDF”')
}
async function deleteReport(item: FormationReportRecord) {
  try { await ElMessageBox.confirm(`删除“${item.name}”后无法恢复,是否继续?`, '确认删除化成报表', { confirmButtonText: '删除', cancelButtonText: '取消', type: 'warning' }); removeReport(item.id); ElMessage.success('化成报表已删除') } catch { /* 用户取消 */ }
}
</script>
 
<template>
  <div class="formation-page">
    <section class="filter-panel"><header class="section-heading"><div><i></i><strong>查询报表</strong><span>FORMATION REPORT QUERY</span></div><small>共 {{ records.length }} 份报表</small></header><div class="filter-grid"><label><span>报表名称</span><el-input v-model="filters.reportName" clearable placeholder="输入报表名称或ID" @keyup.enter="applySearch" /></label><label><span>创建人</span><el-select v-model="filters.creator" clearable placeholder="全部"><el-option label="全部" value="全部" /><el-option v-for="item in creatorOptions" :key="item" :label="item" :value="item" /></el-select></label><label><span>创建时间</span><el-date-picker v-model="filters.dateRange" type="daterange" value-format="YYYY-MM-DD" start-placeholder="开始日期" end-placeholder="结束日期" range-separator="至" unlink-panels /></label><div class="filter-actions"><button class="primary-button" type="button" @click="applySearch"><el-icon><Search /></el-icon>查询</button><button class="secondary-button" type="button" @click="resetSearch"><el-icon><RefreshLeft /></el-icon>重置</button></div></div></section>
    <section class="result-panel"><div class="result-toolbar"><div><i></i><strong>化成报表列表</strong><span>{{ filteredRecords.length }} RESULTS · 按创建时间倒序</span></div><div class="toolbar-actions"><button class="primary-button create-button" type="button" @click="openCreate"><el-icon><Plus /></el-icon>新建报表</button></div></div><div class="table-wrap"><table><thead><tr><th>报表ID</th><th>报表名称</th><th>测试名称</th><th>电池组</th><th>创建时间</th><th>创建人</th><th class="action-column">操作</th></tr></thead><tbody><tr v-for="item in pagedRecords" :key="item.id"><td class="id-cell">#{{ item.id }}</td><td><strong>{{ item.name }}</strong></td><td>{{ item.testName }}</td><td>{{ item.batteryGroup }}</td><td>{{ item.createdAt }}</td><td>{{ item.creator }}</td><td class="action-column"><div class="row-actions"><button type="button" @click="openView(item)">查看报表</button><button type="button" @click="openEdit(item)">编辑</button><button type="button" @click="downloadPdf(item)">下载PDF</button><button class="delete-action" type="button" @click="deleteReport(item)">删除</button></div></td></tr><tr v-if="!pagedRecords.length"><td colspan="7" class="empty-cell">暂无符合条件的化成报表</td></tr></tbody></table></div><footer class="pagination-bar"><span>显示 {{ pagedRecords.length }} 条,共 {{ filteredRecords.length }} 条</span><el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 50]" :total="filteredRecords.length" layout="sizes, prev, pager, next" background /></footer></section>
  </div>
</template>
 
<style scoped>
.formation-page { width: 100%; } .primary-button,.secondary-button { height:34px; display:inline-flex; align-items:center; justify-content:center; gap:6px; padding:0 13px; border:1px solid transparent; border-radius:3px; font-size:13px; font-weight:500; cursor:pointer; white-space:nowrap; }.primary-button{color:#fff;background:var(--blue);border-color:var(--blue)}.primary-button:hover{background:#176fcb;border-color:#176fcb}.secondary-button{color:#52606d;background:#fff;border-color:var(--line-strong)}.secondary-button:hover{color:var(--blue);border-color:#9dc8f5}.filter-panel,.result-panel{background:#fff;border:1px solid #d8e1e9}.filter-panel{margin-bottom:16px}.section-heading,.result-toolbar{display:flex;align-items:center;justify-content:space-between;background:var(--surface-soft);border-bottom:1px solid var(--line)}.section-heading{height:44px;padding:0 16px}.section-heading>div,.result-toolbar>div{display:flex;align-items:center;gap:8px}.section-heading>div:first-child>i,.result-toolbar>div:first-child>i{width:3px;height:16px;background:#2b78a3}.section-heading strong,.result-toolbar strong{color:var(--ink);font-size:15px}.section-heading span,.result-toolbar span{color:#81909d;font-size:10px}.section-heading small{color:#647380;font-size:11px}.filter-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px 18px;padding:16px}.filter-grid label{width:100%;min-width:0;display:grid;grid-template-columns:72px minmax(0,1fr);align-items:center;gap:8px}.filter-grid label>span{color:#52606d;font-size:12px}.filter-grid :deep(.el-select),.filter-grid :deep(.el-date-editor){width:100%}.filter-grid :deep(.el-input__wrapper),.filter-grid :deep(.el-select__wrapper){min-height:34px;border-radius:3px;box-shadow:0 0 0 1px #d8e1e9 inset;background:#f8fafc}.filter-actions{display:flex;justify-content:flex-end;gap:8px}.result-toolbar{height:50px;padding:0 16px}.toolbar-actions{display:flex;align-items:center;gap:8px}.create-button{min-width:96px}.table-wrap{overflow-x:auto}table{width:100%;min-width:1250px;border-collapse:collapse;table-layout:fixed;color:#52606d;font-size:13px}th,td{height:50px;padding:0 12px;border-right:1px solid #dde7f0;border-bottom:1px solid #dde7f0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}th{height:40px;background:#eaf4ff;border-color:#c7e0ff;font-size:12px}th:nth-child(1){width:110px}th:nth-child(2){width:260px}th:nth-child(3){width:230px}th:nth-child(4){width:160px}th:nth-child(5){width:170px}th:nth-child(6){width:100px}th:last-child{width:270px}.action-column{position:sticky;right:0;z-index:2;background:#fff;box-shadow:inset 1px 0 0 #b4c7d9,-8px 0 14px rgba(23,49,74,.12);text-align:center}.row-actions{display:flex;justify-content:center;gap:14px}.row-actions button{border:0;background:transparent;color:var(--blue);font-size:12px;cursor:pointer}.row-actions .delete-action{color:#ad4a4a}.empty-cell{height:160px;text-align:center;color:#8795a3}.pagination-bar{min-height:58px;display:flex;align-items:center;justify-content:space-between;padding:8px 14px 8px 16px;border-top:1px solid var(--line)}.pagination-bar>span{color:#7b8794;font-size:11px}@media(max-width:1100px){.filter-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:680px){.filter-grid{grid-template-columns:1fr}.filter-actions{justify-content:stretch}.filter-actions button{flex:1}.section-heading span,.result-toolbar span{display:none}.pagination-bar{align-items:flex-start;flex-direction:column}}
</style>