|
|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <div class="s8-dept-severity" :style="minHeightStyle">
|
|
|
+ <div class="s8-dept-severity" :class="`s8-dept-severity--${activeTab}`" :style="minHeightStyle">
|
|
|
<div class="s8-dept-severity__header">
|
|
|
<div class="s8-dept-severity__title-wrap">
|
|
|
<span class="s8-dept-severity__code">S8</span>
|
|
|
@@ -11,8 +11,8 @@
|
|
|
<DeptList
|
|
|
:loading="loading"
|
|
|
:rows="seriousRows"
|
|
|
- count-label="严重异常"
|
|
|
empty-text="暂无严重异常"
|
|
|
+ tone="serious"
|
|
|
:count-key="'seriousCount'"
|
|
|
/>
|
|
|
</el-tab-pane>
|
|
|
@@ -20,8 +20,8 @@
|
|
|
<DeptList
|
|
|
:loading="loading"
|
|
|
:rows="followRows"
|
|
|
- count-label="关注异常"
|
|
|
empty-text="暂无关注异常"
|
|
|
+ tone="follow"
|
|
|
:count-key="'followCount'"
|
|
|
/>
|
|
|
</el-tab-pane>
|
|
|
@@ -42,7 +42,6 @@ const props = defineProps<{
|
|
|
type Tab = 'serious' | 'follow';
|
|
|
const activeTab = shallowRef<Tab>('serious');
|
|
|
|
|
|
-// S8-DEPT-CARD-LIST-LAYOUT-FIX-1:严重 / 关注两个视角,分别按 seriousCount / followCount > 0 过滤并降序。
|
|
|
const seriousRows = computed(() =>
|
|
|
[...(props.items ?? [])]
|
|
|
.filter((x) => x && x.seriousCount > 0)
|
|
|
@@ -59,8 +58,8 @@ const minHeightStyle = computed<CSSProperties>(() => {
|
|
|
return { minHeight: `${props.minHeight}px` };
|
|
|
});
|
|
|
|
|
|
-// S8-DEPT-CARD-LIST-LAYOUT-FIX-1:avgProcessHours 无样本(null/undefined/<=0)→ '--';
|
|
|
-// closeRate 缺字段 → '--',0 显示 '0%'(数据真实闭环为 0,不当作空态)。
|
|
|
+// S8-DEPT-CARD-READABLE-LIST-REDESIGN-1:
|
|
|
+// avgProcessHours 无样本(null/undefined/<=0)→ '--';closeRate null → '--',0 显示 '0%'(真实闭环 0 不当空态)。
|
|
|
function formatHoursOrDash(v: number | null | undefined): string {
|
|
|
if (v === null || v === undefined || !Number.isFinite(v) || v <= 0) return '--';
|
|
|
return v >= 24 ? `${Math.round(v)}h+` : `${v.toFixed(1)}h`;
|
|
|
@@ -71,54 +70,49 @@ function formatPercentOrDash(v: number | null | undefined): string {
|
|
|
return `${clamped.toFixed(clamped % 1 === 0 ? 0 : 1)}%`;
|
|
|
}
|
|
|
|
|
|
-// S8-DEPT-CARD-LIST-LAYOUT-FIX-1:窄屏列表方案 —— 每部门一行 card,2 行结构:
|
|
|
-// 第一行:部门名 + 异常数 (右对齐徽标)
|
|
|
-// 第二行:均时 X · 关闭率 Y (副信息行)
|
|
|
-// 与之前 4 列 grid 相比,列宽不再受卡片总宽限制,文字不会挤压。
|
|
|
+// S8-DEPT-CARD-READABLE-LIST-REDESIGN-1:部门指标列表卡。
|
|
|
+// 每条 dept item 两层:
|
|
|
+// 主行:部门名(左、可省略)+ severity badge(右、真正的 pill 徽标)
|
|
|
+// 副行:均时 pill · 关闭率 pill
|
|
|
+// badge 严重红色系 / 关注黄色系;item 背景加深 + 颜色描边,避免与背板融为一体。
|
|
|
const DeptList = (rawProps: {
|
|
|
loading?: boolean;
|
|
|
rows: S8DeptBacklogItem[];
|
|
|
- countLabel: string;
|
|
|
emptyText: string;
|
|
|
+ tone: 'serious' | 'follow';
|
|
|
countKey: 'seriousCount' | 'followCount';
|
|
|
}) => {
|
|
|
if (rawProps.loading) return h('div', { class: 's8-dept-severity__empty' }, '加载中…');
|
|
|
if (!rawProps.rows.length) return h('div', { class: 's8-dept-severity__empty' }, rawProps.emptyText);
|
|
|
+ const badgeText = rawProps.tone === 'serious' ? '严重' : '关注';
|
|
|
return h(
|
|
|
'div',
|
|
|
{ class: 's8-dept-severity__list' },
|
|
|
rawProps.rows.map((row) => {
|
|
|
const count = row[rawProps.countKey];
|
|
|
- const hot = count > 0;
|
|
|
return h(
|
|
|
'div',
|
|
|
- { key: row.deptId, class: 's8-dept-severity__item' },
|
|
|
+ { key: row.deptId, class: ['s8-dept-item', `s8-dept-item--${rawProps.tone}`] },
|
|
|
[
|
|
|
- h('div', { class: 's8-dept-severity__row-primary' }, [
|
|
|
- h('span', { class: 's8-dept-severity__dept-name', title: row.deptName }, row.deptName),
|
|
|
- h('span', { class: 's8-dept-severity__count-wrap' }, [
|
|
|
- h('span', { class: 's8-dept-severity__count-label' }, rawProps.countLabel),
|
|
|
- h(
|
|
|
- 'span',
|
|
|
- {
|
|
|
- class: [
|
|
|
- 's8-dept-severity__count-value',
|
|
|
- hot ? 's8-dept-severity__count-value--hot' : 's8-dept-severity__count-value--zero',
|
|
|
- ],
|
|
|
- },
|
|
|
- String(count),
|
|
|
- ),
|
|
|
- ]),
|
|
|
+ h('div', { class: 's8-dept-item__primary' }, [
|
|
|
+ h('span', { class: 's8-dept-item__name', title: row.deptName }, row.deptName),
|
|
|
+ h(
|
|
|
+ 'span',
|
|
|
+ { class: ['s8-dept-item__badge', `s8-dept-item__badge--${rawProps.tone}`] },
|
|
|
+ [
|
|
|
+ h('span', { class: 's8-dept-item__badge-label' }, badgeText),
|
|
|
+ h('span', { class: 's8-dept-item__badge-value' }, String(count)),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
]),
|
|
|
- h('div', { class: 's8-dept-severity__row-secondary' }, [
|
|
|
- h('span', { class: 's8-dept-severity__metric' }, [
|
|
|
- h('span', { class: 's8-dept-severity__metric-label' }, '均时 '),
|
|
|
- h('span', { class: 's8-dept-severity__metric-value' }, formatHoursOrDash(row.avgProcessHours)),
|
|
|
+ h('div', { class: 's8-dept-item__secondary' }, [
|
|
|
+ h('span', { class: 's8-dept-item__pill' }, [
|
|
|
+ h('span', { class: 's8-dept-item__pill-label' }, '均时'),
|
|
|
+ h('span', { class: 's8-dept-item__pill-value' }, formatHoursOrDash(row.avgProcessHours)),
|
|
|
]),
|
|
|
- h('span', { class: 's8-dept-severity__metric-sep' }, '·'),
|
|
|
- h('span', { class: 's8-dept-severity__metric' }, [
|
|
|
- h('span', { class: 's8-dept-severity__metric-label' }, '关闭率 '),
|
|
|
- h('span', { class: 's8-dept-severity__metric-value' }, formatPercentOrDash(row.closeRate)),
|
|
|
+ h('span', { class: 's8-dept-item__pill' }, [
|
|
|
+ h('span', { class: 's8-dept-item__pill-label' }, '闭环率'),
|
|
|
+ h('span', { class: 's8-dept-item__pill-value' }, formatPercentOrDash(row.closeRate)),
|
|
|
]),
|
|
|
]),
|
|
|
],
|
|
|
@@ -129,13 +123,15 @@ const DeptList = (rawProps: {
|
|
|
DeptList.props = {
|
|
|
loading: { type: Boolean as PropType<boolean>, default: false },
|
|
|
rows: { type: Array as PropType<S8DeptBacklogItem[]>, required: true },
|
|
|
- countLabel: { type: String, required: true },
|
|
|
emptyText: { type: String, required: true },
|
|
|
+ tone: { type: String as PropType<'serious' | 'follow'>, required: true },
|
|
|
countKey: { type: String as PropType<'seriousCount' | 'followCount'>, required: true },
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
-<style scoped>
|
|
|
+<!-- S8-DEPT-CARD-READABLE-LIST-REDESIGN-1:去掉 scoped,因为 DeptList 用 h() 函数式渲染创建的元素不会获得 Vue 注入的 scoped data-attribute;
|
|
|
+ 所有类名已使用 BEM 命名(s8-dept-severity__*, s8-dept-item__*),无冲突风险。 -->
|
|
|
+<style>
|
|
|
.s8-dept-severity {
|
|
|
background: rgba(255, 255, 255, 0.03);
|
|
|
border: 1px solid rgba(255, 255, 255, 0.07);
|
|
|
@@ -149,6 +145,12 @@ DeptList.props = {
|
|
|
height: 100%;
|
|
|
box-sizing: border-box;
|
|
|
overflow: hidden;
|
|
|
+ transition: border-left-color 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+/* S8-DEPT-CARD-READABLE-LIST-REDESIGN-1:左侧 accent 颜色跟随当前 Tab,严重红/关注黄。 */
|
|
|
+.s8-dept-severity--follow {
|
|
|
+ border-left-color: #f59e0b;
|
|
|
}
|
|
|
|
|
|
.s8-dept-severity__header {
|
|
|
@@ -169,6 +171,11 @@ DeptList.props = {
|
|
|
font-weight: 700;
|
|
|
color: #ef4444;
|
|
|
letter-spacing: 0.6px;
|
|
|
+ transition: color 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity--follow .s8-dept-severity__code {
|
|
|
+ color: #f59e0b;
|
|
|
}
|
|
|
|
|
|
.s8-dept-severity__title {
|
|
|
@@ -184,15 +191,15 @@ DeptList.props = {
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__tabs :deep(.el-tabs__header) {
|
|
|
+.s8-dept-severity__tabs .el-tabs__header {
|
|
|
margin: 0 0 6px;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__tabs :deep(.el-tabs__nav-wrap::after) {
|
|
|
+.s8-dept-severity__tabs .el-tabs__nav-wrap::after {
|
|
|
background-color: rgba(255, 255, 255, 0.08);
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__tabs :deep(.el-tabs__item) {
|
|
|
+.s8-dept-severity__tabs .el-tabs__item {
|
|
|
color: #94a3b8;
|
|
|
font-size: 12px;
|
|
|
height: 28px;
|
|
|
@@ -200,45 +207,71 @@ DeptList.props = {
|
|
|
padding: 0 10px;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__tabs :deep(.el-tabs__item.is-active) {
|
|
|
+.s8-dept-severity__tabs .el-tabs__item.is-active {
|
|
|
color: #ef4444;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__tabs :deep(.el-tabs__active-bar) {
|
|
|
+.s8-dept-severity--follow .s8-dept-severity__tabs .el-tabs__item.is-active {
|
|
|
+ color: #f59e0b;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs .el-tabs__active-bar {
|
|
|
background-color: #ef4444;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__tabs :deep(.el-tabs__content) {
|
|
|
+.s8-dept-severity--follow .s8-dept-severity__tabs .el-tabs__active-bar {
|
|
|
+ background-color: #f59e0b;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-severity__tabs .el-tabs__content {
|
|
|
flex: 1;
|
|
|
min-height: 0;
|
|
|
overflow: auto;
|
|
|
}
|
|
|
|
|
|
-/* S8-DEPT-CARD-LIST-LAYOUT-FIX-1:列表方案 */
|
|
|
+/* ── 列表 ── */
|
|
|
.s8-dept-severity__list {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- gap: 4px;
|
|
|
+ gap: 6px;
|
|
|
+ padding-right: 2px;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__item {
|
|
|
+/* ── 单条 item ── */
|
|
|
+.s8-dept-item {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- gap: 3px;
|
|
|
- padding: 6px 8px;
|
|
|
- border-radius: 6px;
|
|
|
- background: rgba(255, 255, 255, 0.02);
|
|
|
+ gap: 6px;
|
|
|
+ padding: 8px 10px;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: rgba(15, 23, 42, 0.5);
|
|
|
+ border: 1px solid rgba(148, 163, 184, 0.1);
|
|
|
+ border-left-width: 2px;
|
|
|
+ transition: background 0.15s ease, border-color 0.15s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-item--serious {
|
|
|
+ border-left-color: rgba(239, 68, 68, 0.55);
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__row-primary {
|
|
|
+.s8-dept-item--follow {
|
|
|
+ border-left-color: rgba(245, 158, 11, 0.55);
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-item:hover {
|
|
|
+ background: rgba(30, 41, 59, 0.6);
|
|
|
+}
|
|
|
+
|
|
|
+/* 主行:部门名 + badge */
|
|
|
+.s8-dept-item__primary {
|
|
|
display: flex;
|
|
|
- align-items: baseline;
|
|
|
+ align-items: center;
|
|
|
justify-content: space-between;
|
|
|
- gap: 8px;
|
|
|
+ gap: 10px;
|
|
|
min-width: 0;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__dept-name {
|
|
|
+.s8-dept-item__name {
|
|
|
color: #e2e8f0;
|
|
|
font-size: 13px;
|
|
|
font-weight: 600;
|
|
|
@@ -249,66 +282,86 @@ DeptList.props = {
|
|
|
flex: 1;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__count-wrap {
|
|
|
+/* badge:严重红 / 关注黄,真正的 pill 徽标,不与部门名连读 */
|
|
|
+.s8-dept-item__badge {
|
|
|
display: inline-flex;
|
|
|
align-items: baseline;
|
|
|
gap: 4px;
|
|
|
+ padding: 2px 8px;
|
|
|
+ border-radius: 999px;
|
|
|
+ font-size: 11px;
|
|
|
+ font-weight: 500;
|
|
|
+ white-space: nowrap;
|
|
|
flex-shrink: 0;
|
|
|
+ border: 1px solid transparent;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__count-label {
|
|
|
- font-size: 11px;
|
|
|
- color: #94a3b8;
|
|
|
+.s8-dept-item__badge--serious {
|
|
|
+ background: rgba(239, 68, 68, 0.14);
|
|
|
+ border-color: rgba(239, 68, 68, 0.35);
|
|
|
+ color: #fca5a5;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__count-value {
|
|
|
+.s8-dept-item__badge--follow {
|
|
|
+ background: rgba(245, 158, 11, 0.14);
|
|
|
+ border-color: rgba(245, 158, 11, 0.35);
|
|
|
+ color: #fcd34d;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-item__badge-label {
|
|
|
+ letter-spacing: 0.5px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-dept-item__badge-value {
|
|
|
font-family: 'Roboto Mono', monospace;
|
|
|
- font-size: 15px;
|
|
|
+ font-size: 14px;
|
|
|
font-weight: 700;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__count-value--hot {
|
|
|
+.s8-dept-item__badge--serious .s8-dept-item__badge-value {
|
|
|
color: #ef4444;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__count-value--zero {
|
|
|
- color: #94a3b8;
|
|
|
+.s8-dept-item__badge--follow .s8-dept-item__badge-value {
|
|
|
+ color: #f59e0b;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__row-secondary {
|
|
|
+/* 副行:均时 / 闭环率 两个独立 pill */
|
|
|
+.s8-dept-item__secondary {
|
|
|
display: flex;
|
|
|
- align-items: baseline;
|
|
|
+ align-items: center;
|
|
|
gap: 6px;
|
|
|
- font-size: 11px;
|
|
|
- color: #94a3b8;
|
|
|
- min-width: 0;
|
|
|
+ flex-wrap: wrap;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__metric {
|
|
|
+.s8-dept-item__pill {
|
|
|
display: inline-flex;
|
|
|
align-items: baseline;
|
|
|
- gap: 2px;
|
|
|
+ gap: 4px;
|
|
|
+ padding: 2px 8px;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: rgba(148, 163, 184, 0.08);
|
|
|
+ border: 1px solid rgba(148, 163, 184, 0.12);
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__metric-label {
|
|
|
+.s8-dept-item__pill-label {
|
|
|
+ font-size: 10.5px;
|
|
|
color: #94a3b8;
|
|
|
+ letter-spacing: 0.3px;
|
|
|
}
|
|
|
|
|
|
-.s8-dept-severity__metric-value {
|
|
|
+.s8-dept-item__pill-value {
|
|
|
font-family: 'Roboto Mono', monospace;
|
|
|
- color: #cbd5e1;
|
|
|
font-size: 12px;
|
|
|
-}
|
|
|
-
|
|
|
-.s8-dept-severity__metric-sep {
|
|
|
- color: rgba(148, 163, 184, 0.5);
|
|
|
+ color: #cbd5e1;
|
|
|
+ font-weight: 500;
|
|
|
}
|
|
|
|
|
|
.s8-dept-severity__empty {
|
|
|
color: #64748b;
|
|
|
font-size: 12px;
|
|
|
- padding: 12px 6px;
|
|
|
+ padding: 16px 6px;
|
|
|
text-align: center;
|
|
|
}
|
|
|
</style>
|