|
|
@@ -0,0 +1,191 @@
|
|
|
+<template>
|
|
|
+ <div class="s8-dept-severity" :style="minHeightStyle">
|
|
|
+ <div class="s8-dept-severity__header">
|
|
|
+ <div class="s8-dept-severity__title-wrap">
|
|
|
+ <span class="s8-dept-severity__code">S8</span>
|
|
|
+ <span class="s8-dept-severity__title">部门严重异常</span>
|
|
|
+ </div>
|
|
|
+ <span class="s8-dept-severity__subtitle">按责任部门聚合</span>
|
|
|
+ </div>
|
|
|
+ <el-tabs v-model="activeTab" class="s8-dept-severity__tabs">
|
|
|
+ <el-tab-pane label="严重" name="serious">
|
|
|
+ <div v-if="loading" class="s8-dept-severity__empty">加载中…</div>
|
|
|
+ <div v-else-if="!sortedItems.length" class="s8-dept-severity__empty">暂无严重异常</div>
|
|
|
+ <ul v-else class="s8-dept-severity__list">
|
|
|
+ <li v-for="row in sortedItems" :key="row.deptId" class="s8-dept-severity__row">
|
|
|
+ <span class="s8-dept-severity__name" :title="row.deptName">{{ row.deptName }}</span>
|
|
|
+ <span
|
|
|
+ class="s8-dept-severity__count"
|
|
|
+ :class="row.seriousCount > 0 ? 's8-dept-severity__count--hot' : 's8-dept-severity__count--zero'"
|
|
|
+ >
|
|
|
+ {{ row.seriousCount }}
|
|
|
+ </span>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { computed, shallowRef, type CSSProperties } from 'vue';
|
|
|
+import type { S8DeptBacklogItem } from '../../api/s8DashboardApi';
|
|
|
+
|
|
|
+const props = defineProps<{
|
|
|
+ items: S8DeptBacklogItem[];
|
|
|
+ loading?: boolean;
|
|
|
+ minHeight?: number;
|
|
|
+}>();
|
|
|
+
|
|
|
+const activeTab = shallowRef<'serious'>('serious');
|
|
|
+
|
|
|
+// TASK-013-DEPT-SERIOUS-CARD-1:seriousCount desc, total desc 兜底;
|
|
|
+// 全员 seriousCount=0 视为空态(模板里 empty 判定)。
|
|
|
+const sortedItems = computed(() => {
|
|
|
+ const filtered = (props.items ?? []).filter((item) => item.seriousCount > 0);
|
|
|
+ return [...filtered].sort((a, b) => {
|
|
|
+ if (b.seriousCount !== a.seriousCount) return b.seriousCount - a.seriousCount;
|
|
|
+ return b.total - a.total;
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+const minHeightStyle = computed<CSSProperties>(() => {
|
|
|
+ if (!props.minHeight || props.minHeight <= 0) return {};
|
|
|
+ return { minHeight: `${props.minHeight}px` };
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.s8-dept-severity {
|
|
|
+ background: rgba(255, 255, 255, 0.03);
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.07);
|
|
|
+ border-left: 3px solid #ef4444;
|
|
|
+ border-radius: 10px;
|
|
|
+ padding: 12px 14px 10px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__header {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__title-wrap {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__code {
|
|
|
+ font-size: 11px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #ef4444;
|
|
|
+ letter-spacing: 0.6px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__title {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #e2e8f0;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__subtitle {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #64748b;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs :deep(.el-tabs__header) {
|
|
|
+ margin: 0 0 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs :deep(.el-tabs__nav-wrap::after) {
|
|
|
+ background-color: rgba(255, 255, 255, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs :deep(.el-tabs__item) {
|
|
|
+ color: #94a3b8;
|
|
|
+ font-size: 12px;
|
|
|
+ height: 28px;
|
|
|
+ line-height: 28px;
|
|
|
+ padding: 0 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs :deep(.el-tabs__item.is-active) {
|
|
|
+ color: #ef4444;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs :deep(.el-tabs__active-bar) {
|
|
|
+ background-color: #ef4444;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs :deep(.el-tabs__content) {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ overflow: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__list {
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ list-style: none;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ padding: 4px 6px;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: rgba(255, 255, 255, 0.02);
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__name {
|
|
|
+ flex: 1;
|
|
|
+ color: #cbd5e1;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__count {
|
|
|
+ min-width: 32px;
|
|
|
+ text-align: right;
|
|
|
+ font-family: 'Roboto Mono', monospace;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__count--hot {
|
|
|
+ color: #ef4444;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__count--zero {
|
|
|
+ color: #94a3b8;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__empty {
|
|
|
+ color: #64748b;
|
|
|
+ font-size: 12px;
|
|
|
+ padding: 12px 6px;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+</style>
|