evoToK3Cloud-vue/src/views/demo/addgongxu/index.vue
2025-09-04 08:59:12 +08:00

957 lines
28 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<dv-full-screen-container :style="{background: '#0f1c2b', color: '#fff'}">
<!-- 顶部标题 -->
<dv-border-box-8 style="height:80px; padding: 10px;">
<div style="font-size: 38px; font-weight: 600; text-align: center; color: #40e0d0;">设计部门任务进度看板</div>
<div style="font-size: 28px;position: absolute; top: 50%; right: 100px; transform: translateY(-50%); font-size: 28px; font-weight: 600; color: #40e0d0;">{{ getCurrentTime() }}</div>
</dv-border-box-8>
<!-- 中间布局 -->
<div style="display:flex; height: calc(100% - 100px); padding: 10px;">
<!-- 左侧轮播表 -->
<dv-border-box-2 style="flex:2; margin-right: 10px; padding:10px;">
<dv-scroll-board :config="tableConfig" style="width:100%; height:100%;"/>
</dv-border-box-2>
<!-- 右侧图表区 -->
<div style="flex:1; display: flex; flex-direction: column; gap: 10px;">
<!-- 超期项目 -->
<dv-border style="flex:1; padding:10px;">
<div style="height: 100%; display: flex; flex-direction: column;">
<!-- 标题和统计信息 -->
<div style="display: flex; justify-content: center; gap: 20px; align-items: center; margin-bottom: 10px;">
<h4 style="margin: 0; color: #ff6b6b; font-size: 24px;font-weight: 600;">超期项目</h4>
<div style="display: flex; gap: 15px; font-size: 18px;font-weight: 600;">
<span style="color: #ff6b6b;">总超期: {{ overdueStats.total }}</span>
<span style="color: #ffa726;">严重超期: {{ overdueStats.critical }}</span>
<span style="color: #42a5f5;">一般超期: {{ overdueStats.normal }}</span>
</div>
</div>
<!-- 超期项目列表 -->
<div class="overdue-list" style="flex: 2; overflow-y: auto;">
<!-- 轮播显示当项目超过3条时启用 -->
<div
v-if="overdueProjects.length > 3"
class="carousel-container"
@mouseenter="stopCarousel"
@mouseleave="startCarousel"
>
<div
v-for="(item, index) in visibleOverdueProjects"
:key="`carousel-${index}`"
class="overdue-item"
:class="getOverdueLevelClass(item.overdueDays)"
>
<div class="item-header">
<span class="project-code">{{ item.projectCode }}</span>
<span class="overdue-days" v-if="item.overdueDays > 0" :class="getOverdueLevelClass(item.overdueDays)">
超期{{ item.overdueDays }}天
</span>
</div>
<div class="item-content">
<div class="designer">{{ item.designer }}</div>
<div class="description">{{ item.description }}</div>
<div class="time-info">
<span>开始: {{ item.startDate }}</span>
<span>计划: {{ item.planDate }}</span>
</div>
</div>
</div>
</div>
<!-- 普通显示当项目不超过1条时 -->
<div v-else>
<div
v-for="(item, index) in overdueProjects"
:key="index"
class="overdue-item"
:class="getOverdueLevelClass(item.overdueDays)"
>
<div class="item-header">
<span class="project-code">{{ item.projectCode }}</span>
<span class="overdue-days" :class="getOverdueLevelClass(item.overdueDays)">
{{ item.overdueDays * -1 }}天
</span>
</div>
<div class="item-content">
<div class="designer">{{ item.designer }}</div>
<div class="description">{{ item.description }}</div>
<div class="time-info">
<span>开始: {{ item.startDate }}</span>
<span>计划: {{ item.planDate }}</span>
</div>
</div>
</div>
</div>
<!-- 无超期项目时的提示 -->
<div v-if="overdueProjects.length === 0" class="no-overdue">
<div style="text-align: center; color: #4caf50; padding: 20px;">
<i class="el-icon-success" style="font-size: 24px;"></i>
<div style="margin-top: 10px;">暂无超期项目</div>
</div>
</div>
</div>
</div>
</dv-border>
<!-- 临期项目排名轮播表 -->
<dv-border-box-10 style="flex:1; padding:10px;">
<div style="height: 100%; display: flex; flex-direction: column;">
<!-- 标题 -->
<div style="text-align: center; margin-bottom: 10px;">
<h3 style="margin: 0; color: #ffa726; font-size: 20px;">临期项目</h3>
</div>
<!-- 临期项目轮播表 -->
<div class="deadline-list" style="flex: 1; overflow: hidden;">
<div
v-if="overdueProjects.length > 3"
class="deadline-carousel"
@mouseenter="stopDeadlineCarousel"
@mouseleave="startDeadlineCarousel"
>
<div
v-for="(item, index) in visibleDeadlineProjects"
:key="`deadline-${index}`"
class="deadline-item"
:class="getDeadlineLevelClass(item.overdueDays)"
>
<div class="rank-number">{{ item.rank }}</div>
<div class="item-info">
<div class="designer-name" style="flex: 1;">{{ item.designer }}</div>
<div style="display: flex;flex: 1;">
<div v-if="item.overdueDays > 0" class="deadline-days" :class="getDeadlineLevelClass(item.overdueDays)">临期{{ item.overdueDays }}天</div>
</div>
<div class="customer-center">{{item.projectCode}}</div>
<div class="customer-center">{{item.planDate}}</div>
</div>
</div>
<!-- 轮播指示器 -->
<div class="deadline-indicators">
<span
v-for="(_, index) in deadlinePages"
:key="index"
class="deadline-indicator"
:class="{ active: index === currentDeadlineIndex }"
@click="goToDeadlinePage(index)"
></span>
</div>
</div>
<!-- 普通显示当项目不超过4条时 -->
<div v-else>
<div
v-for="(item, index) in overdueProjects"
:key="index"
class="deadline-item"
:class="getDeadlineLevelClass(item.overdueDays)"
>
<div class="rank-number">{{ index + 1 }}</div>
<div class="item-info">
<div class="designer-name">{{ item.designer }}</div>
<div class="deadline-days" :class="getDeadlineLevelClass(item.overdueDays)">
临期{{ item.overdueDays }}天
</div>
</div>
</div>
</div>
<!-- 无临期项目时的提示 -->
<div v-if="overdueProjects.length === 0" class="no-deadline">
<div style="text-align: center; color: #4caf50; padding: 20px;">
<i class="el-icon-success" style="font-size: 24px;"></i>
<div style="margin-top: 10px;">暂无临期项目</div>
</div>
</div>
</div>
</div>
</dv-border-box-10>
<!-- 项目数量热力图(周/月/年 x 设计人) -->
<dv-border-box-13 style="flex:1; padding:10px;">
<div style="display:flex; justify-content: space-between; align-items:center; margin-bottom: 8px;">
<h4 style="margin:0; color:#40e0d0;">项目方案数</h4>
<el-radio-group v-model="timeGranularity" size="mini">
<el-radio-button label="week">周</el-radio-button>
<el-radio-button label="month">月</el-radio-button>
<el-radio-button label="year">年</el-radio-button>
</el-radio-group>
</div>
<dv-capsule-chart :config="capsuleConfig" style="width:100%; height:calc(100% - 30px);" />
</dv-border-box-13>
</div>
</div>
<!-- Loading -->
<dv-loading v-if="loading">加载中...</dv-loading>
</dv-full-screen-container>
</template>
<script>
import { listProPlan, listProPlanOverdue ,listProPlanExpirys} from '@/api/system/proPlan'
export default {
data () {
return {
loading: false,
timer: null, // 定时器
carouselTimer: null, // 轮播定时器
currentCarouselIndex: 0, // 当前轮播索引
carouselInterval: 4000, // 轮播间隔时间(毫秒)
deadlineTimer: null, // 临期项目轮播定时器
currentDeadlineIndex: 0, // 当前临期项目轮播索引
deadlineInterval: 4000, // 临期项目轮播间隔时间(毫秒)
// 超期项目数据
overdueProjects: [],
// 临期项目数据
expiryOverdueProjects: [],
tableConfig: {
header: ['执行令号/方案号', '设计人', '内容', '开始时间', '完成时间', '重要程度'],
data: [],
index: true,
// columnWidth: [120, 200, 230, 400, 150, 150, 200, 100],
align: ['center','center','center','center','center','center','center'],
rowNum: 8,
waitTime: 1500,
hoverPause:false
}
,
timeGranularity: 'month'
}
},
created() {
// 初始化超期项目数据,动态计算超期天数
this.initProjects();
// 拉取方案列表填充滚动表格
this.fetchProPlanList();
// 拉取超期项目
this.fetchOverdue();
// 拉取临期项目
this.ExpiryOverdue();
},
mounted() {
// 每小时更新一次天数
this.timer = setInterval(() => {
this.initProjects();
}, 60 * 60 * 1000); // 1小时
// 启动轮播当项目超过3条时
this.startCarousel();
// 启动临期项目轮播当项目超过4条时
this.startDeadlineCarousel();
},
beforeDestroy() {
// 清理定时器
if (this.timer) {
clearInterval(this.timer);
}
if (this.carouselTimer) {
clearInterval(this.carouselTimer);
}
if (this.deadlineTimer) {
clearInterval(this.deadlineTimer);
}
},
computed: {
// 超期项目统计
overdueStats() {
console.log(this.overdueProjects);
const total = this.overdueProjects.length;
const critical = this.overdueProjects.filter(item => item.overdueDays > 5).length;
const normal = this.overdueProjects.filter(item => item.overdueDays <= 5).length;
return { total, critical, normal };
},
// 轮播相关计算属性
carouselPages() {
return Math.ceil(this.overdueProjects.length / 3);
},
visibleOverdueProjects() {
const start = this.currentCarouselIndex * 3;
const end = start + 3;
return this.overdueProjects.slice(start, end);
},
// 临期项目相关计算属性
deadlinePages() {
return Math.ceil(this.expiryOverdueProjects.length / 3);
},
visibleDeadlineProjects() {
const start = this.currentDeadlineIndex * 3;
const end = start + 3;
return this.expiryOverdueProjects.slice(start, end).map((item, index) => ({
...item,
rank: start + index + 1
}));
},
heatmapOption() {
const designers = this.getUniqueDesigners();
const timeLabels = this.getTimeLabels(this.timeGranularity);
const indexOfDesigner = new Map();
designers.forEach((d, i) => indexOfDesigner.set(d, i));
const indexOfTime = new Map();
timeLabels.forEach((t, i) => indexOfTime.set(t, i));
const counts = Array.from({ length: designers.length }, () => Array(timeLabels.length).fill(0));
(this.tableConfig.data || []).forEach(row => {
const designer = row[1];
const dateStr = row[4];
if (!designer || !dateStr) return;
const label = this.formatTimeLabel(dateStr, this.timeGranularity);
if (!indexOfDesigner.has(designer) || !indexOfTime.has(label)) return;
counts[indexOfDesigner.get(designer)][indexOfTime.get(label)] += 1;
});
const seriesData = [];
let maxVal = 0;
for (let y = 0; y < designers.length; y++) {
for (let x = 0; x < timeLabels.length; x++) {
const val = counts[y][x];
maxVal = Math.max(maxVal, val);
seriesData.push([x, y, val]);
}
}
return {
backgroundColor: 'transparent',
tooltip: {
position: 'top',
formatter: (p) => {
const t = timeLabels[p.data[0]];
const d = designers[p.data[1]];
const v = p.data[2];
return `${d}<br/>${t}: ${v}`;
}
},
grid: { top: 20, right: 10, bottom: 20, left: 80 },
xAxis: {
type: 'category',
data: timeLabels,
axisLabel: { color: '#e2e8f0' },
axisLine: { lineStyle: { color: '#334155' } },
splitLine: { show: false }
},
yAxis: {
type: 'category',
data: designers,
axisLabel: { color: '#e2e8f0' },
axisLine: { lineStyle: { color: '#334155' } },
splitLine: { show: false }
},
visualMap: {
min: 0,
max: Math.max(5, maxVal),
calculable: true,
orient: 'vertical',
right: 0,
top: 'middle',
textStyle: { color: '#e2e8f0' },
inRange: {
color: ['#0f172a', '#22d3ee']
}
},
series: [
{
type: 'heatmap',
data: seriesData,
label: { show: false },
emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.5)' } }
}
]
};
},
capsuleConfig() {
const designers = this.getUniqueDesigners();
const timeLabels = this.getTimeLabels(this.timeGranularity);
const latestLabel = timeLabels[timeLabels.length - 1];
const countsByDesigner = new Map();
designers.forEach(d => countsByDesigner.set(d, 0));
(this.tableConfig.data || []).forEach(row => {
const designer = row[1];
const dateStr = row[4];
if (!designer || !dateStr) return;
const label = this.formatTimeLabel(dateStr, this.timeGranularity);
if (label === latestLabel && countsByDesigner.has(designer)) {
countsByDesigner.set(designer, countsByDesigner.get(designer) + 1);
}
});
const data = Array.from(countsByDesigner.entries())
.map(([name, value]) => ({ name, value }))
.sort((a, b) => b.value - a.value);
return {
data,
colors: ['#ef4444', '#f59e0b', '#22c55e', '#3b82f6', '#8b5cf6', '#06b6d4', '#ea580c'],
unit: '项',
showValue: true
};
}
},
methods: {
getCurrentTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份是从0开始的所以加1
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
// 初始化项目数据
initProjects() {
const today = new Date();
// 超期项目天数更新
this.overdueProjects.forEach(project => {
const planDate = new Date(project.planDate);
const diffTime = planDate.getTime() - today.getTime();
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
// 超期天数:正数显示超期天数,负数不显示
project.overdueDays = diffDays > 0 ? diffDays : null;
});
// 临期项目天数更新
this.expiryOverdueProjects.forEach(project => {
const planDate = new Date(project.planDate);
const diffTime = planDate.getTime() - today.getTime();
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
// 临期天数:正数显示剩余天数,负数不显示
project.overdueDays = diffDays > 0 ? diffDays : null;
});
// 按临期天数排序,临期时间短的排在前面
// this.overdueProjects.sort((a, b) => {
// if (a.overdueDays === null) return 1;
// if (b.overdueDays === null) return -1;
// return a.overdueDays - b.overdueDays;
// });
// 重新启动超期项目轮播
this.startCarousel();
// 重新启动临期项目轮播
this.startDeadlineCarousel();
},
// 获取超期等级样式类
getOverdueLevelClass(overdueDays) {
if (overdueDays > 5) {
return 'critical-overdue';
} else if (overdueDays > 2) {
return 'normal-overdue';
} else {
return 'light-overdue';
}
},
// 获取临期等级样式类
getDeadlineLevelClass(deadlineDays) {
if (deadlineDays > 5) {
return 'critical-deadline';
} else if (deadlineDays > 2) {
return 'normal-deadline';
} else {
return 'light-deadline';
}
},
// 轮播相关方法
goToPage(index) {
this.currentCarouselIndex = index;
},
// 临期项目轮播相关方法
goToDeadlinePage(index) {
this.currentDeadlineIndex = index;
},
// 启动临期项目轮播
startDeadlineCarousel() {
// 先停止现有的轮播
this.stopDeadlineCarousel();
if (this.expiryOverdueProjects.length > 4) {
this.deadlineTimer = setInterval(() => {
this.nextDeadlinePage();
}, this.deadlineInterval);
}
},
// 停止临期项目轮播
stopDeadlineCarousel() {
if (this.deadlineTimer) {
clearInterval(this.deadlineTimer);
this.deadlineTimer = null;
}
},
// 临期项目下一页
nextDeadlinePage() {
if (this.deadlinePages > 1) {
this.currentDeadlineIndex = (this.currentDeadlineIndex + 1) % this.deadlinePages;
}
},
// 临期项目上一页
prevDeadlinePage() {
if (this.deadlinePages > 1) {
this.currentDeadlineIndex = this.currentDeadlineIndex === 0
? this.deadlinePages - 1
: this.currentDeadlineIndex - 1;
}
},
// 启动轮播
startCarousel() {
// 先停止现有的轮播
this.stopCarousel();
if (this.overdueProjects.length > 3) {
this.carouselTimer = setInterval(() => {
this.nextPage();
}, this.carouselInterval);
}
},
// 停止轮播
stopCarousel() {
if (this.carouselTimer) {
clearInterval(this.carouselTimer);
this.carouselTimer = null;
}
},
// 下一页
nextPage() {
if (this.carouselPages > 1) {
this.currentCarouselIndex = (this.currentCarouselIndex + 1) % this.carouselPages;
}
},
// 上一页
prevPage() {
if (this.carouselPages > 1) {
this.currentCarouselIndex = this.currentCarouselIndex === 0
? this.carouselPages - 1
: this.currentCarouselIndex - 1;
}
},
// 获取临期数据
async ExpiryOverdue(){
try {
this.loading = true;
const res = await listProPlanExpirys();
const list = res?.rows || res || [];
this.expiryOverdueProjects = (list || []).map(item => ({
projectCode: item.projectCode || item.code || '-',
designer: item.designer || '-',
description: item.description || item.remark || '',
startDate: (item.startDate || '').toString().slice(0, 10),
planDate: (item.planDate || item.endDate || '').toString().slice(0, 10),
overdueDays: Number(item.overdueDays ?? 0)
}));
// 排序与轮播刷新
this.expiryOverdueProjects.sort((a, b) => b.overdueDays - a.overdueDays);
this.startDeadlineCarousel();
} catch (e) {
console.error('fetchOverdue error:', e);
} finally {
this.loading = false;
}
},
// 从后端加载超期数据并刷新视图
async fetchOverdue() {
try {
this.loading = true;
const res = await listProPlanOverdue();
const list = res?.rows || res || [];
this.overdueProjects = (list || []).map(item => ({
projectCode: item.projectCode || item.code || '-',
designer: item.designer || '-',
description: item.description || item.remark || '',
startDate: (item.startDate || '').toString().slice(0, 10),
planDate: (item.planDate || item.endDate || '').toString().slice(0, 10),
overdueDays: Number(item.overdueDays ?? 0)
}));
// 排序与轮播刷新
this.overdueProjects.sort((a, b) => b.overdueDays - a.overdueDays);
this.startCarousel();
} catch (e) {
console.error('fetchOverdue error:', e);
} finally {
this.loading = false;
}
},
// 拉取方案列表,填充左侧滚动表格
async fetchProPlanList() {
try {
const { rows = [] } = await listProPlan();
// 映射为表格行:[执行令号, 设计人, 内容, 开始时间, 完成时间, 重要程度]
const mapped = rows.map(item => [
item.projectCode || '-',
item.designer || '-',
item.remark || '-',
item.startDate || '-',
item.endDate || '-',
'—'
]);
this.tableConfig = {
...this.tableConfig,
data: mapped
};
} catch (e) {
console.error('fetchProPlanList error:', e);
}
},
getUniqueDesigners() {
const set = new Set();
(this.tableConfig.data || []).forEach(row => { if (row[1]) set.add(row[1]); });
(this.overdueProjects || []).forEach(item => { if (item.designer) set.add(item.designer); });
(this.expiryOverdueProjects || []).forEach(item => { if (item.designer) set.add(item.designer); });
return Array.from(set);
},
getTimeLabels(granularity) {
const labelsSet = new Set();
const add = (dateStr) => {
if (!dateStr) return;
const lbl = this.formatTimeLabel(dateStr, granularity);
if (lbl) labelsSet.add(lbl);
};
(this.tableConfig.data || []).forEach(row => {
add(row[4]);
});
const labels = Array.from(labelsSet);
labels.sort((a, b) => a.localeCompare(b));
return labels;
},
formatTimeLabel(dateStr, granularity) {
const d = new Date(dateStr);
if (isNaN(d.getTime())) return '';
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
if (granularity === 'year') {
return `${year}`;
}
if (granularity === 'month') {
return `${year}-${month}`;
}
const { week, isoYear } = this.getISOWeek(d);
return `${isoYear}-W${String(week).padStart(2, '0')}`;
},
getISOWeek(dateObj) {
const d = new Date(Date.UTC(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
const weekNo = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
return { week: weekNo, isoYear: d.getUTCFullYear() };
}
}
}
</script>
<style>
body, html, #app {
margin: 0;
height: 100%;
background: #0f1c2b;
}
.grid-item {
margin: 30px;
}
/* 超期项目样式 */
.overdue-list {
scrollbar-width: thin;
scrollbar-color: #4a5568 #2d3748;
}
.overdue-list::-webkit-scrollbar {
width: 6px;
}
.overdue-list::-webkit-scrollbar-track {
background: #2d3748;
border-radius: 3px;
}
.overdue-list::-webkit-scrollbar-thumb {
background: #4a5568;
border-radius: 3px;
}
.overdue-list::-webkit-scrollbar-thumb:hover {
background: #718096;
}
.overdue-item {
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 12px;
margin-bottom: 10px;
border-left: 4px solid;
transition: all 0.3s ease;
}
.overdue-item:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateX(5px);
}
.overdue-item.critical-overdue {
border-left-color: #ff6b6b;
background: rgba(255, 107, 107, 0.1);
}
.overdue-item.normal-overdue {
border-left-color: #ffa726;
background: rgba(255, 167, 38, 0.1);
}
.overdue-item.light-overdue {
border-left-color: #42a5f5;
background: rgba(66, 165, 245, 0.1);
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.project-code {
font-weight: bold;
color: #fff;
font-size: 20px;
}
.overdue-days {
font-weight: bold;
font-size: 12px;
padding: 2px 8px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.2);
}
.overdue-days.critical-overdue {
color: #ff6b6b;
background: rgba(255, 107, 107, 0.2);
}
.overdue-days.normal-overdue {
color: #ffa726;
background: rgba(255, 167, 38, 0.2);
}
.overdue-days.light-overdue {
color: #42a5f5;
background: rgba(66, 165, 245, 0.2);
}
.item-content {
font-size: 12px;
}
.designer {
color: #40e0d0;
font-weight: bold;
margin-bottom: 19px;
}
.description {
color: #e2e8f0;
margin-bottom: 6px;
line-height: 1.3;
}
.time-info {
display: flex;
gap: 15px;
color: #a0aec0;
font-size: 11px;
}
.no-overdue {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
/* 轮播容器样式 */
.carousel-container {
position: relative;
height: 100%;
}
/* 轮播项目动画 */
.overdue-item {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 临期项目样式 */
.deadline-list {
scrollbar-width: thin;
scrollbar-color: #4a5568 #2d3748;
}
.deadline-list::-webkit-scrollbar {
width: 6px;
}
.deadline-list::-webkit-scrollbar-track {
background: #2d3748;
border-radius: 3px;
}
.deadline-list::-webkit-scrollbar-thumb {
background: #4a5568;
border-radius: 3px;
}
.deadline-list::-webkit-scrollbar-thumb:hover {
background: #718096;
}
.deadline-item {
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 12px;
margin-bottom: 10px;
border-left: 4px solid;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 10px;
}
.deadline-item:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateX(5px);
}
.deadline-item.critical-deadline {
border-left-color: #ff6b6b;
background: rgba(255, 107, 107, 0.1);
}
.deadline-item.normal-deadline {
border-left-color: #ffa726;
background: rgba(255, 167, 38, 0.1);
}
.deadline-item.light-deadline {
border-left-color: #42a5f5;
background: rgba(66, 165, 245, 0.1);
}
.rank-number {
font-weight: bold;
font-size: 18px;
color: #40e0d0;
min-width: 30px;
text-align: center;
}
.item-info {
display: flex;
flex: 1;
justify-content: space-around;
gap: 20px;
justify-items: center;
text-align: center;
align-items: center;
}
.designer-name {
font-weight: bold;
color: #fff;
font-size: 18px;
margin-bottom: 4px;
}
.customer-center{
display: flex;
align-items: center;
flex: 1;
margin: 0 auto;
}
.deadline-days {
margin: 0 auto;
font-weight: bold;
font-size: 16px;
padding: 5px 8px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.2);
}
.deadline-days.critical-deadline {
color: #ff6b6b;
background: rgba(255, 107, 107, 0.2);
}
.deadline-days.normal-deadline {
color: #ffa726;
background: rgba(255, 167, 38, 0.2);
}
.deadline-days.light-deadline {
color: #42a5f5;
background: rgba(66, 165, 245, 0.2);
}
.header{
font-size: 22px !important;
font-weight: 600 !important;
}
.row-item {
font-size: 20px !important;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>