<template>
|
|
<div class="month-view">
|
|
<div class="month-header">
|
|
<h2>{{ displayYear }}年{{ displayMonth }}月</h2>
|
|
<div class="month-controls">
|
|
<el-date-picker
|
|
v-model="selectedDate"
|
|
style="float: right; width: 120px; border: 0"
|
|
popper-class="card-date-editor"
|
|
type="month"
|
|
size="mini"
|
|
placeholder="选择月"
|
|
:picker-options="pickerOptions"
|
|
@change="onMonthChange"
|
|
>
|
|
</el-date-picker>
|
|
<div class="nav-buttons">
|
|
<button class="nav-btn" @click="prevMonth"><</button>
|
|
<button class="nav-btn" @click="nextMonth">></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="calendar-grid">
|
|
<div class="weekday-header">
|
|
<div class="weekday" v-for="day in weekdays" :key="day">{{ day }}</div>
|
|
</div>
|
|
|
|
<div class="days-grid">
|
|
<!-- 上月剩余天数 -->
|
|
<div
|
|
class="day-cell empty"
|
|
v-for="empty in firstDayOffset"
|
|
:key="'empty-' + empty"
|
|
></div>
|
|
|
|
<!-- 当月天数 -->
|
|
<div
|
|
class="day-cell"
|
|
v-for="day in daysInMonth"
|
|
:key="day"
|
|
:class="{ today: isToday(day) }"
|
|
>
|
|
<div class="day-header">
|
|
<div class="day-number">{{ day }}</div>
|
|
</div>
|
|
<div class="tasks-list" v-if="hasTasks(day)">
|
|
<div
|
|
class="task-item"
|
|
v-for="(task, index) in getTasksForDay(day)"
|
|
:key="index"
|
|
:class="`status-${task.status}`"
|
|
@mouseenter="showTooltip($event, task.name)"
|
|
@mouseleave="hideTooltip"
|
|
>
|
|
<span class="task-name">{{ task.name }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 下月补充天数 -->
|
|
<div
|
|
class="day-cell empty"
|
|
v-for="empty in lastDayOffset"
|
|
:key="'empty-end-' + empty"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 工具提示 -->
|
|
<div v-if="tooltipVisible" class="task-tooltip" :style="tooltipStyle">
|
|
{{ tooltipText }}
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { calenderMonth } from "@/api/videosMonitor/index";
|
|
|
|
export default {
|
|
name: "MonthView",
|
|
props: {
|
|
year: {
|
|
type: Number,
|
|
required: false,
|
|
default: null,
|
|
},
|
|
month: {
|
|
type: Number,
|
|
required: false,
|
|
default: null,
|
|
},
|
|
},
|
|
data() {
|
|
const today = new Date();
|
|
const currentYear = today.getFullYear();
|
|
const currentMonth = today.getMonth() + 1;
|
|
|
|
// console.log('初始化当前日期:', currentYear, currentMonth);
|
|
|
|
return {
|
|
// 内部维护的当前显示年月(默认当前月)
|
|
displayYear: currentYear,
|
|
displayMonth: currentMonth,
|
|
selectedDate: new Date(currentYear, currentMonth - 1, 1),
|
|
pickerOptions: {},
|
|
weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
|
|
tasksData: [],
|
|
loading: false,
|
|
// 工具提示相关
|
|
tooltipVisible: false,
|
|
tooltipText: "",
|
|
tooltipStyle: {
|
|
left: "0px",
|
|
top: "0px",
|
|
},
|
|
currentUpdatePosition: null,
|
|
// 防抖标记
|
|
fetchTimeout: null,
|
|
// 标记是否已经执行过首次数据加载
|
|
initialDataLoaded: false,
|
|
};
|
|
},
|
|
computed: {
|
|
daysInMonth() {
|
|
return new Date(this.displayYear, this.displayMonth, 0).getDate();
|
|
},
|
|
firstDayOffset() {
|
|
return new Date(this.displayYear, this.displayMonth - 1, 1).getDay();
|
|
},
|
|
lastDayOffset() {
|
|
const totalCells =
|
|
Math.ceil((this.firstDayOffset + this.daysInMonth) / 7) * 7;
|
|
return totalCells - this.firstDayOffset - this.daysInMonth;
|
|
},
|
|
},
|
|
watch: {
|
|
displayYear: {
|
|
handler(newYear, oldYear) {
|
|
if (newYear && oldYear && newYear !== oldYear) {
|
|
this.updateSelectedDate();
|
|
this.debouncedFetchData();
|
|
// 同步到父组件
|
|
this.$emit("update:year", newYear);
|
|
}
|
|
},
|
|
immediate: false,
|
|
},
|
|
displayMonth: {
|
|
handler(newMonth, oldMonth) {
|
|
if (newMonth && oldMonth && newMonth !== oldMonth) {
|
|
this.updateSelectedDate();
|
|
this.debouncedFetchData();
|
|
// 同步到父组件
|
|
this.$emit("update:month", newMonth);
|
|
}
|
|
},
|
|
immediate: false,
|
|
},
|
|
// 监听父组件传入的props,只有当父组件想要控制时才同步到内部状态
|
|
// 但我们需要一个标志来判断是否应该使用父组件的值
|
|
year: {
|
|
handler(newYear) {
|
|
// 只有当父组件明确想要控制(即传入了非null/undefined的值)且
|
|
// 组件还没有初始化完成时,才使用父组件的值
|
|
// 或者我们可以让父组件通过一个prop来控制是否使用父组件的值
|
|
// 注意:这里我们不自动更新displayYear,而是由父组件通过prop控制
|
|
// 或者我们可以在created中一次性处理
|
|
},
|
|
immediate: false,
|
|
},
|
|
month: {
|
|
handler(newMonth) {
|
|
// 同样,不自动更新
|
|
},
|
|
immediate: false,
|
|
},
|
|
},
|
|
created() {
|
|
const today = new Date();
|
|
const currentYear = today.getFullYear();
|
|
const currentMonth = today.getMonth() + 1;
|
|
|
|
// 关键修改:始终使用当前日期作为默认值
|
|
// 只有当父组件明确传入了有效的年月,并且我们确定要使用父组件的值时,才使用父组件的值
|
|
// 但根据您的需求,您想要显示当前月,所以我们应该忽略父组件的传入值
|
|
|
|
// 方案1:完全忽略父组件传入的值,始终使用当前日期
|
|
this.displayYear = currentYear;
|
|
this.displayMonth = currentMonth;
|
|
|
|
|
|
// 通知父组件当前月份(如果父组件需要知道)
|
|
this.$emit("update:year", this.displayYear);
|
|
this.$emit("update:month", this.displayMonth);
|
|
|
|
this.updateSelectedDate();
|
|
},
|
|
mounted() {
|
|
// 只在首次挂载时获取数据(确保当前月份数据加载)
|
|
if (!this.initialDataLoaded) {
|
|
this.getMonthData();
|
|
this.initialDataLoaded = true;
|
|
}
|
|
// 监听窗口变化,重新计算工具提示位置
|
|
window.addEventListener("resize", this.handleResize);
|
|
},
|
|
beforeDestroy() {
|
|
// 清理事件监听
|
|
window.removeEventListener("resize", this.handleResize);
|
|
// 清理防抖定时器
|
|
if (this.fetchTimeout) {
|
|
clearTimeout(this.fetchTimeout);
|
|
}
|
|
// 清理鼠标移动监听
|
|
if (this.currentUpdatePosition) {
|
|
document.removeEventListener("mousemove", this.currentUpdatePosition);
|
|
}
|
|
},
|
|
methods: {
|
|
// 更新选中的日期
|
|
updateSelectedDate() {
|
|
if (this.displayYear && this.displayMonth) {
|
|
this.selectedDate = new Date(this.displayYear, this.displayMonth - 1, 1);
|
|
}
|
|
},
|
|
|
|
// 防抖获取数据
|
|
debouncedFetchData() {
|
|
if (this.fetchTimeout) {
|
|
clearTimeout(this.fetchTimeout);
|
|
}
|
|
this.fetchTimeout = setTimeout(() => {
|
|
this.getMonthData();
|
|
}, 300);
|
|
},
|
|
|
|
onMonthChange() {
|
|
if (this.selectedDate) {
|
|
const year = this.selectedDate.getFullYear();
|
|
const month = this.selectedDate.getMonth() + 1;
|
|
|
|
// 更新内部状态
|
|
if (year !== this.displayYear || month !== this.displayMonth) {
|
|
this.displayYear = year;
|
|
this.displayMonth = month;
|
|
// 注意:不需要在这里调用getMonthData,watch会触发
|
|
}
|
|
}
|
|
},
|
|
|
|
prevMonth() {
|
|
const newDate = new Date(this.displayYear, this.displayMonth - 2, 1);
|
|
this.displayYear = newDate.getFullYear();
|
|
this.displayMonth = newDate.getMonth() + 1;
|
|
},
|
|
|
|
nextMonth() {
|
|
const newDate = new Date(this.displayYear, this.displayMonth, 1);
|
|
this.displayYear = newDate.getFullYear();
|
|
this.displayMonth = newDate.getMonth() + 1;
|
|
},
|
|
|
|
isToday(day) {
|
|
const today = new Date();
|
|
return (
|
|
today.getFullYear() === this.displayYear &&
|
|
today.getMonth() + 1 === this.displayMonth &&
|
|
today.getDate() === day
|
|
);
|
|
},
|
|
|
|
hasTasks(day) {
|
|
return this.tasksData.some((task) => {
|
|
if (!task.start) return false;
|
|
const taskDate = new Date(task.start);
|
|
return (
|
|
taskDate.getDate() === day &&
|
|
taskDate.getMonth() + 1 === this.displayMonth &&
|
|
taskDate.getFullYear() === this.displayYear
|
|
);
|
|
});
|
|
},
|
|
|
|
getTasksForDay(day) {
|
|
return this.tasksData
|
|
.filter((task) => {
|
|
if (!task.start) return false;
|
|
const taskDate = new Date(task.start);
|
|
return (
|
|
taskDate.getDate() === day &&
|
|
taskDate.getMonth() + 1 === this.displayMonth &&
|
|
taskDate.getFullYear() === this.displayYear
|
|
);
|
|
})
|
|
.map((task) => ({
|
|
name: task.name || "未命名任务",
|
|
status: this.mapTaskStatus(task.status),
|
|
}));
|
|
},
|
|
|
|
getTaskStats(day) {
|
|
const dayTasks = this.getTasksForDay(day);
|
|
const stats = {
|
|
completed: 0,
|
|
executing: 0,
|
|
"not-executed": 0,
|
|
};
|
|
dayTasks.forEach((task) => {
|
|
if (stats.hasOwnProperty(task.status)) {
|
|
stats[task.status]++;
|
|
}
|
|
});
|
|
return {
|
|
stats,
|
|
total: dayTasks.length,
|
|
};
|
|
},
|
|
|
|
mapTaskStatus(apiStatus) {
|
|
const statusMap = {
|
|
executed: "completed",
|
|
executing: "executing",
|
|
pending: "not-executed",
|
|
};
|
|
return statusMap[apiStatus] || "not-executed";
|
|
},
|
|
|
|
async getMonthData() {
|
|
// 避免重复请求
|
|
if (this.loading) return;
|
|
|
|
try {
|
|
this.loading = true;
|
|
|
|
const response = await calenderMonth({
|
|
year: this.displayYear,
|
|
month: this.displayMonth,
|
|
});
|
|
|
|
// 处理不同的响应格式
|
|
const data = response?.rows || response?.data || response || [];
|
|
this.tasksData = Array.isArray(data) ? data : [];
|
|
} catch (error) {
|
|
this.tasksData = [];
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
// 显示工具提示
|
|
showTooltip(event, text) {
|
|
this.tooltipText = text;
|
|
this.tooltipVisible = true;
|
|
|
|
// 使用当前鼠标位置
|
|
const x = event.clientX;
|
|
const y = event.clientY;
|
|
const offset = 10;
|
|
|
|
this.tooltipStyle = {
|
|
left: `${x + offset}px`,
|
|
top: `${y + offset}px`,
|
|
};
|
|
|
|
// 监听鼠标移动来更新位置
|
|
const updatePosition = (e) => {
|
|
if (this.tooltipVisible) {
|
|
this.tooltipStyle = {
|
|
left: `${e.clientX + offset}px`,
|
|
top: `${e.clientY + offset}px`,
|
|
};
|
|
}
|
|
};
|
|
|
|
document.addEventListener("mousemove", updatePosition);
|
|
this.currentUpdatePosition = updatePosition;
|
|
},
|
|
|
|
// 隐藏工具提示
|
|
hideTooltip() {
|
|
this.tooltipVisible = false;
|
|
if (this.currentUpdatePosition) {
|
|
document.removeEventListener("mousemove", this.currentUpdatePosition);
|
|
this.currentUpdatePosition = null;
|
|
}
|
|
},
|
|
|
|
// 处理窗口大小变化
|
|
handleResize() {
|
|
if (this.tooltipVisible) {
|
|
this.tooltipVisible = false;
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.month-view {
|
|
width: 100%;
|
|
min-height: 100%;
|
|
background-color: #172533;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.month-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
background-color: #101d29;
|
|
padding: 15px 20px;
|
|
border-radius: 8px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.month-header h2 {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
color: #fff !important;
|
|
}
|
|
|
|
.month-controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
|
|
.nav-buttons {
|
|
display: flex;
|
|
gap: 5px;
|
|
}
|
|
|
|
.nav-btn {
|
|
width: 80px;
|
|
height: 30px;
|
|
background-color: #202f3e;
|
|
color: #fff;
|
|
border: 2px solid #40486a;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.nav-btn:hover {
|
|
background-color: #40486a;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.calendar-grid {
|
|
background-color: #101d29;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
border: 2px solid #40486a;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
.weekday-header {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
background-color: #101d29;
|
|
padding: 10px 0;
|
|
border-bottom: 2px solid #40486a;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.weekday {
|
|
text-align: center;
|
|
font-weight: bold;
|
|
color: #fff;
|
|
padding: 5px;
|
|
border-right: 2px solid #40486a;
|
|
}
|
|
|
|
.weekday:last-child {
|
|
border-right: none;
|
|
}
|
|
|
|
.days-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
grid-template-rows: repeat(auto-fill, minmax(120px, 1fr));
|
|
background-color: #172533;
|
|
flex: 1;
|
|
min-height: 0;
|
|
}
|
|
|
|
/* 固定日历格子的宽度和高度自适应 */
|
|
.day-cell {
|
|
padding: 5px;
|
|
position: relative;
|
|
min-height: 120px;
|
|
border-right: 2px solid #40486a;
|
|
border-bottom: 2px solid #40486a;
|
|
background-color: #172533;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 确保所有格子等宽 */
|
|
.day-cell {
|
|
width: 100%;
|
|
min-width: 0; /* 允许宽度压缩 */
|
|
}
|
|
|
|
/* 响应式:小屏幕时适当调整最小高度 */
|
|
@media (max-width: 1200px) {
|
|
.day-cell {
|
|
min-height: 100px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 992px) {
|
|
.day-cell {
|
|
min-height: 90px;
|
|
}
|
|
}
|
|
|
|
/* 行背景色保持不变 */
|
|
.days-grid > .day-cell:nth-child(7n + 1):nth-child(-n + 7),
|
|
.days-grid > .day-cell:nth-child(7n + 2):nth-child(-n + 7),
|
|
.days-grid > .day-cell:nth-child(7n + 3):nth-child(-n + 7),
|
|
.days-grid > .day-cell:nth-child(7n + 4):nth-child(-n + 7),
|
|
.days-grid > .day-cell:nth-child(7n + 5):nth-child(-n + 7),
|
|
.days-grid > .day-cell:nth-child(7n + 6):nth-child(-n + 7),
|
|
.days-grid > .day-cell:nth-child(7n + 7):nth-child(-n + 7) {
|
|
background-color: #172533;
|
|
}
|
|
|
|
/* 偶数行颜色 */
|
|
.days-grid > .day-cell:nth-child(7n + 1):nth-child(n + 8),
|
|
.days-grid > .day-cell:nth-child(7n + 2):nth-child(n + 8),
|
|
.days-grid > .day-cell:nth-child(7n + 3):nth-child(n + 8),
|
|
.days-grid > .day-cell:nth-child(7n + 4):nth-child(n + 8),
|
|
.days-grid > .day-cell:nth-child(7n + 5):nth-child(n + 8),
|
|
.days-grid > .day-cell:nth-child(7n + 6):nth-child(n + 8),
|
|
.days-grid > .day-cell:nth-child(7n + 7):nth-child(n + 8) {
|
|
background-color: #202f3e;
|
|
}
|
|
|
|
/* 第三行颜色 */
|
|
.days-grid > .day-cell:nth-child(7n + 1):nth-child(n + 15),
|
|
.days-grid > .day-cell:nth-child(7n + 2):nth-child(n + 15),
|
|
.days-grid > .day-cell:nth-child(7n + 3):nth-child(n + 15),
|
|
.days-grid > .day-cell:nth-child(7n + 4):nth-child(n + 15),
|
|
.days-grid > .day-cell:nth-child(7n + 5):nth-child(n + 15),
|
|
.days-grid > .day-cell:nth-child(7n + 6):nth-child(n + 15),
|
|
.days-grid > .day-cell:nth-child(7n + 7):nth-child(n + 15) {
|
|
background-color: #172533;
|
|
}
|
|
|
|
/* 第四行颜色 */
|
|
.days-grid > .day-cell:nth-child(7n + 1):nth-child(n + 22),
|
|
.days-grid > .day-cell:nth-child(7n + 2):nth-child(n + 22),
|
|
.days-grid > .day-cell:nth-child(7n + 3):nth-child(n + 22),
|
|
.days-grid > .day-cell:nth-child(7n + 4):nth-child(n + 22),
|
|
.days-grid > .day-cell:nth-child(7n + 5):nth-child(n + 22),
|
|
.days-grid > .day-cell:nth-child(7n + 6):nth-child(n + 22),
|
|
.days-grid > .day-cell:nth-child(7n + 7):nth-child(n + 22) {
|
|
background-color: #202f3e;
|
|
}
|
|
|
|
/* 第五行颜色 */
|
|
.days-grid > .day-cell:nth-child(7n + 1):nth-child(n + 29),
|
|
.days-grid > .day-cell:nth-child(7n + 2):nth-child(n + 29),
|
|
.days-grid > .day-cell:nth-child(7n + 3):nth-child(n + 29),
|
|
.days-grid > .day-cell:nth-child(7n + 4):nth-child(n + 29),
|
|
.days-grid > .day-cell:nth-child(7n + 5):nth-child(n + 29),
|
|
.days-grid > .day-cell:nth-child(7n + 6):nth-child(n + 29),
|
|
.days-grid > .day-cell:nth-child(7n + 7):nth-child(n + 29) {
|
|
background-color: #172533;
|
|
}
|
|
|
|
/* 第六行颜色 */
|
|
.days-grid > .day-cell:nth-child(7n + 1):nth-child(n + 36),
|
|
.days-grid > .day-cell:nth-child(7n + 2):nth-child(n + 36),
|
|
.days-grid > .day-cell:nth-child(7n + 3):nth-child(n + 36),
|
|
.days-grid > .day-cell:nth-child(7n + 4):nth-child(n + 36),
|
|
.days-grid > .day-cell:nth-child(7n + 5):nth-child(n + 36),
|
|
.days-grid > .day-cell:nth-child(7n + 6):nth-child(n + 36),
|
|
.days-grid > .day-cell:nth-child(7n + 7):nth-child(n + 36) {
|
|
background-color: #202f3e;
|
|
}
|
|
|
|
/* 去掉最右边的边框 */
|
|
.day-cell:nth-child(7n) {
|
|
border-right: none;
|
|
}
|
|
|
|
/* 去掉最后一行的底部边框 */
|
|
.days-grid > .day-cell:nth-last-child(-n + 7) {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.day-cell.empty {
|
|
background-color: #101d29 !important;
|
|
border-right: 2px solid #40486a;
|
|
border-bottom: 2px solid #40486a;
|
|
}
|
|
|
|
.day-cell.today {
|
|
background-color: rgba(36, 115, 130, 0.2) !important;
|
|
border: 1px solid #247382;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* 新增:日期+统计的头部容器,弹性布局左右分布 */
|
|
.day-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 5px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.day-number {
|
|
font-size: 25px;
|
|
font-weight: bold;
|
|
color: #7984b1;
|
|
line-height: 1;
|
|
}
|
|
|
|
/* 新增:任务统计样式 - 小字体、对应颜色、横向排列 */
|
|
.task-stats {
|
|
display: flex;
|
|
gap: 4px;
|
|
}
|
|
.stat-item {
|
|
font-size: 10px; /* 小字体 */
|
|
padding: 1px 4px;
|
|
border-radius: 2px;
|
|
min-width: 16px;
|
|
text-align: center;
|
|
}
|
|
|
|
.tasks-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
min-height: 0;
|
|
max-height: calc(100% - 30px);
|
|
}
|
|
|
|
/* 任务较多时的自适应高度 */
|
|
.day-cell:has(.tasks-list) {
|
|
min-height: 120px;
|
|
}
|
|
|
|
.day-cell:has(.task-item:nth-child(n + 4)) {
|
|
min-height: 140px;
|
|
}
|
|
|
|
.day-cell:has(.task-item:nth-child(n + 6)) {
|
|
min-height: 160px;
|
|
}
|
|
|
|
.day-cell:has(.task-item:nth-child(n + 8)) {
|
|
min-height: 180px;
|
|
}
|
|
|
|
.task-item {
|
|
padding: 2px 4px;
|
|
border-radius: 3px;
|
|
font-size: 12px;
|
|
cursor: default;
|
|
transition: all 0.3s ease;
|
|
flex-shrink: 0;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
.task-name {
|
|
display: block;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
width: 100%;
|
|
}
|
|
|
|
.status-executing {
|
|
background-color: #247382;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.status-completed {
|
|
background-color: #296a5a;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.status-not-executed {
|
|
background-color: #6d7d92;
|
|
color: #ffffff;
|
|
}
|
|
|
|
/* 工具提示样式 */
|
|
.task-tooltip {
|
|
position: fixed;
|
|
z-index: 9999;
|
|
background-color: #202f3e;
|
|
border: 2px solid #40486a;
|
|
border-radius: 4px;
|
|
padding: 8px 12px;
|
|
font-size: 12px;
|
|
color: #ffffff;
|
|
max-width: 300px;
|
|
word-break: break-all;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
pointer-events: none;
|
|
animation: fadeIn 0.2s ease;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-5px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* 滚动条样式 */
|
|
.tasks-list::-webkit-scrollbar {
|
|
width: 4px;
|
|
}
|
|
|
|
.tasks-list::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.tasks-list::-webkit-scrollbar-thumb {
|
|
background: #40486a;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.tasks-list::-webkit-scrollbar-thumb:hover {
|
|
background: #7984b1;
|
|
}
|
|
|
|
/* 覆盖Element UI的默认样式 */
|
|
:deep(.el-date-editor--month) {
|
|
background-color: #101d29;
|
|
border: none;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
:deep(.el-input__inner) {
|
|
background-color: #101d29;
|
|
color: #7984b1;
|
|
border: 2px solid #40486a;
|
|
}
|
|
|
|
:deep(.el-input__inner::placeholder) {
|
|
color: #6d7d92;
|
|
}
|
|
|
|
:deep(.el-date-picker__header-label) {
|
|
color: #7984b1;
|
|
}
|
|
|
|
:deep(.el-date-picker__month-btn) {
|
|
color: #7984b1;
|
|
}
|
|
|
|
:deep(.el-date-picker__month-btn:hover) {
|
|
color: #247382;
|
|
}
|
|
|
|
:deep(.el-picker-panel) {
|
|
background-color: #101d29;
|
|
border: 2px solid #40486a;
|
|
}
|
|
|
|
.card-date-editor {
|
|
background-color: #101d29;
|
|
}
|
|
|
|
/* Blue dashboard theme */
|
|
.month-view {
|
|
background: #0b1248;
|
|
color: #dceaff;
|
|
}
|
|
|
|
.month-header {
|
|
margin-bottom: 14px;
|
|
padding: 10px 0;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
}
|
|
|
|
.month-header h2 {
|
|
color: #b9d6ff !important;
|
|
}
|
|
|
|
.nav-btn {
|
|
width: 36px;
|
|
border: 1px solid #438fe5;
|
|
border-radius: 0;
|
|
color: #9dcbff;
|
|
background: #0b1d58;
|
|
}
|
|
|
|
.nav-btn:hover {
|
|
border-color: #55baff;
|
|
color: #ffffff;
|
|
background: #123b82;
|
|
}
|
|
|
|
.calendar-grid {
|
|
border: 1px solid #2787ed;
|
|
border-radius: 0;
|
|
background: #0b174f;
|
|
}
|
|
|
|
.weekday-header {
|
|
padding: 0;
|
|
border-bottom: 1px solid #2787ed;
|
|
background: #0b174f;
|
|
}
|
|
|
|
.weekday {
|
|
padding: 12px 5px;
|
|
border-right: 1px solid #2787ed;
|
|
color: #35bcff;
|
|
}
|
|
|
|
.days-grid,
|
|
.days-grid > .day-cell {
|
|
background: #101e63 !important;
|
|
}
|
|
|
|
.days-grid > .day-cell:nth-child(14n + 8),
|
|
.days-grid > .day-cell:nth-child(14n + 9),
|
|
.days-grid > .day-cell:nth-child(14n + 10),
|
|
.days-grid > .day-cell:nth-child(14n + 11),
|
|
.days-grid > .day-cell:nth-child(14n + 12),
|
|
.days-grid > .day-cell:nth-child(14n + 13),
|
|
.days-grid > .day-cell:nth-child(14n + 14) {
|
|
background: #13266f !important;
|
|
}
|
|
|
|
.day-cell {
|
|
border-right: 1px solid #2787ed;
|
|
border-bottom: 1px solid #2787ed;
|
|
}
|
|
|
|
.day-cell.empty {
|
|
border-right: 1px solid #2787ed;
|
|
border-bottom: 1px solid #2787ed;
|
|
background: #091442 !important;
|
|
}
|
|
|
|
.day-number {
|
|
color: #8096ce;
|
|
}
|
|
|
|
.day-cell.today {
|
|
z-index: 2;
|
|
background: linear-gradient(135deg, rgba(13, 99, 173, 0.72), rgba(13, 65, 139, 0.72)) !important;
|
|
border: 2px solid #54ffff !important;
|
|
box-shadow: inset 0 0 20px rgba(84, 255, 255, 0.18), 0 0 8px rgba(84, 255, 255, 0.42);
|
|
}
|
|
|
|
.day-cell.today .day-number {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
color: #ffffff;
|
|
text-shadow: 0 0 6px rgba(84, 255, 255, 0.75);
|
|
}
|
|
|
|
.day-cell.today .day-number::after {
|
|
content: "今";
|
|
display: inline-grid;
|
|
place-items: center;
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 50%;
|
|
color: #062653;
|
|
background: #54ffff;
|
|
font-size: 11px;
|
|
line-height: 1;
|
|
text-shadow: none;
|
|
}
|
|
|
|
.status-executing { background-color: #147fc1; }
|
|
.status-completed { background-color: #178a70; }
|
|
.status-not-executed { background-color: #536c9f; }
|
|
|
|
.task-tooltip {
|
|
border: 1px solid #438fe5;
|
|
border-radius: 2px;
|
|
background: #0b1d58;
|
|
}
|
|
|
|
:deep(.el-date-editor--month),
|
|
:deep(.el-input__inner) {
|
|
border-radius: 0;
|
|
background-color: #0b1d58;
|
|
}
|
|
|
|
:deep(.el-input__inner) {
|
|
border: 1px solid #438fe5;
|
|
color: #dceaff;
|
|
}
|
|
</style>
|