Lhistory.vue 8.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. <template>
  2. <div class="overview-page" v-loading="loading">
  3. <section class="page-head"><div><span class="eyebrow">HISTORICAL SIGNALS</span><h1>历史预警概况</h1><p>数据更新:{{ result.generatedAt || '正在加载' }}</p></div><el-button type="primary" :loading="loading" @click="loadData"><el-icon><Refresh /></el-icon>刷新概况</el-button></section>
  4. <el-alert v-if="errorMessage" :title="errorMessage" type="error" show-icon closable class="error-alert" @close="errorMessage = ''" />
  5. <div class="history-layout">
  6. <el-card shadow="never" class="total-card"><span>累计预警总量</span><strong>{{ totalCount }}</strong><small>统计全部非草稿预警事件</small><div class="total-line"><i :style="{ width: specialStats.length ? '100%' : '0%' }" /></div></el-card>
  7. <el-card shadow="never" class="chart-card"><template #header><div class="card-title"><span>专项预警分布</span><em>{{ specialStats.length }} 个专项</em></div></template><div ref="chartRef" class="chart-box" /><el-empty v-if="!specialStats.length && !loading" description="暂无专项统计数据" /></el-card>
  8. </div>
  9. <el-card shadow="never" class="table-card">
  10. <template #header><div class="card-title"><div><span>历史预警明细</span><em>{{ total }} 条</em></div><div class="filters"><el-select v-model="selectedSpecial" clearable placeholder="全部预警专项" @change="resetPage"><el-option v-for="item in specialStats" :key="item.specialName" :label="item.specialName" :value="item.specialName" /></el-select><el-input v-model="keyword" clearable placeholder="搜索编号、专项或单位" @keyup.enter="resetPage" @clear="resetPage"><template #prefix><el-icon><Search /></el-icon></template></el-input><el-button type="primary" @click="resetPage">查询</el-button></div></div></template>
  11. <el-table v-loading="tableLoading" :data="rows" stripe highlight-current-row empty-text="暂无符合条件的历史预警" @row-click="openDetail">
  12. <el-table-column label="预警编号" min-width="190" show-overflow-tooltip><template #default="{ row }">{{ row.warningNo || row.warning_no || '预警编号未生成' }}</template></el-table-column>
  13. <el-table-column label="预警类型" width="120"><template #default="{ row }">{{ warningTypeText(row) }}</template></el-table-column>
  14. <el-table-column label="预警专项" min-width="150" show-overflow-tooltip><template #default="{ row }">{{ row.warningSpecial || row.warning_special || '未设置' }}</template></el-table-column>
  15. <el-table-column label="预警级别" width="105" align="center"><template #default="{ row }"><el-tag :type="warningLevelTag(row)">{{ warningLevelText(row) }}</el-tag></template></el-table-column>
  16. <el-table-column label="权属单位" min-width="150" show-overflow-tooltip><template #default="{ row }">{{ row.ownershipUnit || row.ownership_unit || '未设置' }}</template></el-table-column>
  17. <el-table-column label="发布时间" min-width="165"><template #default="{ row }">{{ formatDashboardTime(row.publishTime || row.publish_time || row.createTime) }}</template></el-table-column>
  18. <el-table-column label="状态" width="105" align="center"><template #default="{ row }"><el-tag :type="warningStatusTag(row)" effect="plain">{{ warningStatusText(row) }}</el-tag></template></el-table-column>
  19. <el-table-column label="操作" width="90" fixed="right" align="center"><template #default="{ row }"><el-button link type="primary" @click.stop="openDetail(row)">查看详情</el-button></template></el-table-column>
  20. </el-table>
  21. <el-pagination v-if="total" v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[10,20,50]" layout="total, sizes, prev, pager, next, jumper" :total="total" class="pagination" @current-change="loadPage" @size-change="handleSizeChange" />
  22. </el-card>
  23. <WarningDetailDrawer v-model="detailVisible" :row="selectedRow" />
  24. </div>
  25. </template>
  26. <script setup name="WarningHistoryOverview">
  27. import { loadWarningReferences } from '../lstatistics/statisticsShared'
  28. import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
  29. import { ElMessage } from 'element-plus'
  30. import { Refresh, Search } from '@element-plus/icons-vue'
  31. import * as echarts from 'echarts'
  32. import { getWarningHistoryOverview, pageWarningHistory } from '@/api/warning/dashboard'
  33. import WarningDetailDrawer from './WarningDetailDrawer.vue'
  34. import { dashboardData, formatDashboardTime, pageRecords, pageTotal, warningLevelTag, warningLevelText, warningStatusTag, warningStatusText, warningTypeText } from './dashboardShared'
  35. const loading=ref(false);const tableLoading=ref(false);const errorMessage=ref('');const result=ref({totalCount:0,specialStats:[],detailList:[]});const keyword=ref('');const selectedSpecial=ref('');const pageNum=ref(1);const pageSize=ref(10);const rows=ref([]);const total=ref(0);const chartRef=ref();const detailVisible=ref(false);const selectedRow=ref(null);let chart
  36. const totalCount=computed(()=>Number(result.value.totalCount||0));const specialStats=computed(()=>(result.value.specialStats||[]).map(item=>({specialName:item.specialName||item.special_name||'未分类',count:Number(item.count||0),percentage:Number(item.percentage||0)})))
  37. function openDetail(row){selectedRow.value=row;detailVisible.value=true}function resetPage(){pageNum.value=1;loadPage()}function resize(){chart?.resize()}
  38. function handleSizeChange(){pageNum.value=1;loadPage()}
  39. function renderChart(){if(!chartRef.value||!specialStats.value.length){chart?.clear();return}chart ||= echarts.init(chartRef.value);chart.setOption({tooltip:{trigger:'item',formatter:'{b}<br/>{c} 条 ({d}%)'},legend:{type:'scroll',orient:'vertical',right:4,top:'center',textStyle:{color:'#52637a'}},series:[{type:'pie',radius:['46%','70%'],center:['38%','52%'],avoidLabelOverlap:true,itemStyle:{borderColor:'#fff',borderWidth:3},label:{show:false},emphasis:{label:{show:true,fontSize:15,fontWeight:'bold'}},data:specialStats.value.map(item=>({name:item.specialName,value:item.count}))}],color:['#2563eb','#0f766e','#f59e0b','#dc2626','#64748b','#7c3aed']})}
  40. async function loadPage(){tableLoading.value=true;errorMessage.value='';try{const response=await pageWarningHistory({pageNum:pageNum.value,pageSize:pageSize.value,warningSpecial:selectedSpecial.value||undefined,keyword:keyword.value.trim()||undefined});rows.value=pageRecords(response);total.value=pageTotal(response)}catch(error){rows.value=[];total.value=0;errorMessage.value=error?.message||'历史预警明细加载失败';ElMessage.error(errorMessage.value)}finally{tableLoading.value=false}}
  41. async function loadData(){loading.value=true;errorMessage.value='';try{ await loadWarningReferences();result.value={...result.value,...dashboardData(await getWarningHistoryOverview(),{})};await nextTick();renderChart();await loadPage()}catch(error){errorMessage.value=error?.message||'历史预警概况加载失败,请稍后重试';ElMessage.error(errorMessage.value)}finally{loading.value=false}}
  42. onMounted(()=>{loadData();window.addEventListener('resize',resize)});onUnmounted(()=>{window.removeEventListener('resize',resize);chart?.dispose()})
  43. </script>
  44. <style scoped lang="scss">
  45. .overview-page{min-height:calc(100vh - 84px);padding:22px;background:#f4f7fb;color:#172b4d}.page-head{display:flex;justify-content:space-between;align-items:flex-end;gap:18px;margin-bottom:20px}.eyebrow{color:#0f766e;font-size:11px;font-weight:700;letter-spacing:2px}.page-head h1{margin:6px 0 4px;font-size:26px}.page-head p{margin:0;color:#718096}.error-alert{margin-bottom:16px}.history-layout{display:grid;grid-template-columns:minmax(250px,.65fr) minmax(460px,1.5fr);gap:16px;margin-bottom:16px}.total-card{border-top:3px solid #0f766e}.total-card :deep(.el-card__body){display:flex;min-height:290px;flex-direction:column;justify-content:center;padding:30px}.total-card span{color:#718096}.total-card strong{margin:14px 0 10px;font-size:48px;line-height:1}.total-card small{color:#94a3b8}.total-line{height:5px;margin-top:28px;overflow:hidden;border-radius:4px;background:#e6edf4}.total-line i{display:block;height:100%;background:#0f766e}.chart-card{min-height:320px}.chart-box{height:250px}.card-title{display:flex;justify-content:space-between;align-items:center;gap:16px;font-weight:650}.card-title>div:first-child{display:flex;align-items:baseline;gap:10px}.card-title em{color:#94a3b8;font-size:12px;font-style:normal;font-weight:400}.filters{display:flex;gap:10px}.filters .el-select{width:190px}.filters .el-input{width:240px}.pagination{justify-content:flex-end;margin-top:16px}@media(max-width:900px){.history-layout{grid-template-columns:1fr}.page-head{align-items:flex-start;flex-direction:column}.card-title{align-items:flex-start;flex-direction:column}.filters{width:100%;flex-wrap:wrap}.filters .el-select,.filters .el-input{width:100%}}
  46. </style>