Files
yl/生产任务看板/src/views/plan-kanban/components/CenterDetailPanel.vue
2026-07-20 18:47:17 +08:00

206 lines
4.6 KiB
Vue

<template>
<section class="center-detail-panel panel">
<header class="detail-panel-head">
<h2>{{ title }}</h2>
<span v-if="status === 'ready'" class="detail-count">{{ rows.length }} </span>
</header>
<div class="detail-table-head detail-grid-row" :style="gridStyle">
<span v-for="column in columns" :key="column.key">{{ column.label }}</span>
</div>
<div class="detail-table-body">
<div
v-if="status === 'ready'"
class="detail-table-track"
:class="{ scrolling: shouldScroll }"
:style="scrollStyle"
>
<div
v-for="(row, index) in displayRows"
:key="`${row.id}-${index}`"
class="detail-grid-row detail-data-row"
:class="{ urgent: row.urgent }"
:style="gridStyle"
:title="row.title"
>
<span
v-for="column in columns"
:key="column.key"
:class="[`cell-${column.key}`, { 'category-cell': column.key === 'category' }]"
>
{{ row[column.key] || '--' }}
</span>
</div>
</div>
<div v-else class="detail-panel-state">
<span v-if="status === 'loading'" class="detail-loading-icon"></span>
{{ status === 'loading' ? '加载中' : message }}
</div>
</div>
</section>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
title: { type: String, required: true },
columns: { type: Array, required: true },
rows: { type: Array, default: () => [] },
status: { type: String, default: 'loading' },
message: { type: String, default: '' },
visibleRows: { type: Number, default: 9 }
})
const rowHeight = 34
const shouldScroll = computed(() => props.rows.length > props.visibleRows)
const displayRows = computed(() => shouldScroll.value ? [...props.rows, ...props.rows] : props.rows)
const gridStyle = computed(() => ({
gridTemplateColumns: props.columns.map(column => column.width || '1fr').join(' ')
}))
const scrollStyle = computed(() => ({
'--scroll-distance': `${props.rows.length * rowHeight}px`,
'--scroll-duration': `${Math.max(props.rows.length * 1.8, 20)}s`
}))
</script>
<style scoped lang="scss">
.center-detail-panel {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
overflow: hidden;
color: #dcecff;
background: rgba(7, 31, 72, 0.72);
}
.detail-panel-head {
display: flex;
flex: 0 0 36px;
align-items: center;
justify-content: space-between;
min-width: 0;
padding: 0 10px;
border-bottom: 1px solid rgba(43, 216, 255, 0.28);
}
.detail-panel-head h2 {
margin: 0;
color: #8cd3ff;
font-size: 14px;
font-weight: 900;
letter-spacing: 0;
white-space: nowrap;
}
.detail-count {
color: #1ce6ff;
font-size: 10px;
}
.detail-grid-row {
display: grid;
align-items: center;
min-width: 0;
text-align: center;
}
.detail-table-head {
flex: 0 0 30px;
color: #bfe8ff;
font-size: 9px;
font-weight: 800;
background: rgba(10, 75, 145, 0.76);
}
.detail-table-head span,
.detail-data-row > span {
min-width: 0;
padding: 0 2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.detail-table-body {
position: relative;
flex: 1;
min-height: 0;
overflow: hidden;
}
.detail-table-track.scrolling {
animation: detail-table-scroll var(--scroll-duration) linear infinite;
}
.detail-table-track.scrolling:hover {
animation-play-state: paused;
}
.detail-data-row {
height: 34px;
color: #dcecff;
font-size: 9px;
border-bottom: 1px solid rgba(43, 216, 255, 0.1);
}
.detail-data-row:nth-child(odd) {
background: rgba(4, 25, 59, 0.38);
}
.detail-data-row:nth-child(even) {
background: rgba(4, 18, 47, 0.24);
}
.detail-data-row.urgent,
.detail-data-row.urgent .category-cell {
color: #ff637a;
background: rgba(116, 19, 37, 0.22);
}
.category-cell {
color: #1ce6ff;
font-weight: 800;
}
.cell-materialName {
display: -webkit-box;
overflow-wrap: anywhere;
line-height: 12px;
white-space: normal;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.detail-panel-state {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
height: 100%;
color: #78a9ce;
font-size: 12px;
}
.detail-loading-icon {
width: 14px;
height: 14px;
border: 2px solid rgba(43, 216, 255, 0.25);
border-top-color: #2bd8ff;
border-radius: 50%;
animation: detail-loading 0.8s linear infinite;
}
@keyframes detail-table-scroll {
from { transform: translateY(0); }
to { transform: translateY(calc(-1 * var(--scroll-distance))); }
}
@keyframes detail-loading {
to { transform: rotate(360deg); }
}
</style>