You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
743 lines
17 KiB
743 lines
17 KiB
|
2 weeks ago
|
<template>
|
||
|
|
<basic-container>
|
||
|
|
<avue-form :option="option" @submit="submit" @error="error">
|
||
|
|
<template #menu-form>
|
||
|
|
<el-button type="primary" icon="el-icon-search" @click="handleSubmit"> 搜索 </el-button>
|
||
|
|
<el-button icon="el-icon-delete" @click="handleSubmit"> 清空 </el-button>
|
||
|
|
</template>
|
||
|
|
</avue-form>
|
||
|
|
|
||
|
|
<div class="gantt-container">
|
||
|
|
<!-- 头部标题和图例 -->
|
||
|
|
<div class="gantt-header">
|
||
|
|
<!-- <h2>设备生产任务甘特图</h2> -->
|
||
|
|
<div class="status-legend">
|
||
|
|
<div class="legend-item">
|
||
|
|
<span class="legend-color completed"></span>
|
||
|
|
<span>已完成</span>
|
||
|
|
</div>
|
||
|
|
<div class="legend-item">
|
||
|
|
<span class="legend-color processing"></span>
|
||
|
|
<span>进行中</span>
|
||
|
|
</div>
|
||
|
|
<div class="legend-item">
|
||
|
|
<span class="legend-color pending"></span>
|
||
|
|
<span>未开始</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 甘特图主体 -->
|
||
|
|
<div class="gantt-wrapper">
|
||
|
|
<!-- 左侧设备列表 -->
|
||
|
|
<div class="device-list">
|
||
|
|
<div class="device-item device-item-title" :style="{ height: '36px' }">设备</div>
|
||
|
|
<div
|
||
|
|
v-for="(device, index) in devices"
|
||
|
|
:key="index"
|
||
|
|
class="device-item"
|
||
|
|
:style="{ height: rowHeight + 'px' }"
|
||
|
|
>
|
||
|
|
{{ device }}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 右侧时间轴 (时间在上,刻度线在下) -->
|
||
|
|
<div class="timeline-container" @wheel.prevent="handleWheel">
|
||
|
|
<!-- 图表X轴区域(时间在上,刻度线在下) -->
|
||
|
|
<div class="chart-axis">
|
||
|
|
<!-- 时间标签 -->
|
||
|
|
<div class="time-labels" :style="{ width: `${timelineWidth}%` }">
|
||
|
|
<!-- 主刻度标签(小时) -->
|
||
|
|
<div
|
||
|
|
v-for="(time, index) in majorTickLabels"
|
||
|
|
:key="index"
|
||
|
|
class="major-label"
|
||
|
|
:style="{ left: `${(index / 24) * 100}%` }"
|
||
|
|
>
|
||
|
|
{{ time }}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 副刻度标签(30分钟,放大后显示) -->
|
||
|
|
<div v-if="zoomLevel >= 2" class="minor-labels">
|
||
|
|
<div
|
||
|
|
v-for="(time, index) in minorTickLabels"
|
||
|
|
:key="index"
|
||
|
|
class="minor-label"
|
||
|
|
:style="{ left: `${(index / (24 * 2)) * 100}%` }"
|
||
|
|
>
|
||
|
|
{{ time }}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 刻度线(在下方) -->
|
||
|
|
<div class="tick-lines" :style="{ width: `${timelineWidth}%` }">
|
||
|
|
<!-- 主刻度线(小时) -->
|
||
|
|
<div
|
||
|
|
v-for="(time, index) in majorTickLabels"
|
||
|
|
:key="index"
|
||
|
|
class="major-tick-line"
|
||
|
|
:style="{ left: `${(index / 24) * 100}%` }"
|
||
|
|
:title="time"
|
||
|
|
></div>
|
||
|
|
|
||
|
|
<!-- 副刻度线(30分钟,放大后显示) -->
|
||
|
|
<div v-if="zoomLevel >= 2" class="minor-tick-lines">
|
||
|
|
<div
|
||
|
|
v-for="(time, index) in minorTickLabels"
|
||
|
|
:key="index"
|
||
|
|
class="minor-tick-line"
|
||
|
|
:style="{ left: `${(index / (24 * 2)) * 100}%` }"
|
||
|
|
:title="time"
|
||
|
|
></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- X轴基线 -->
|
||
|
|
<div class="axis-base-line"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 甘特图内容区域 -->
|
||
|
|
<div class="chart-content" :style="{ width: `${timelineWidth}%` }">
|
||
|
|
<!-- 网格线 -->
|
||
|
|
<div class="grid-lines">
|
||
|
|
<div
|
||
|
|
v-for="(time, index) in majorTickLabels"
|
||
|
|
:key="index"
|
||
|
|
class="grid-line"
|
||
|
|
:style="{ left: `${(index / 24) * 100}%` }"
|
||
|
|
></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 任务容器 -->
|
||
|
|
<div class="tasks-container">
|
||
|
|
<div
|
||
|
|
v-for="(device, devIndex) in devices"
|
||
|
|
:key="devIndex"
|
||
|
|
class="device-task-row"
|
||
|
|
:style="{ height: rowHeight + 'px' }"
|
||
|
|
>
|
||
|
|
<div
|
||
|
|
v-for="(task, taskIndex) in getDeviceTasks(device)"
|
||
|
|
:key="taskIndex"
|
||
|
|
class="task-bar"
|
||
|
|
:style="{
|
||
|
|
left: `${getPositionPercent(task.start)}%`,
|
||
|
|
width: `${getWidthPercent(task.start, task.end)}%`,
|
||
|
|
backgroundColor: getStatusColor(task.status),
|
||
|
|
}"
|
||
|
|
@mouseenter="showTooltip($event, task, device)"
|
||
|
|
@mouseleave="hideTooltip()"
|
||
|
|
>
|
||
|
|
<span class="task-label">{{ task.task }}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 悬浮提示框 -->
|
||
|
|
<div
|
||
|
|
v-if="tooltipVisible"
|
||
|
|
class="tooltip"
|
||
|
|
:style="{
|
||
|
|
left: `${tooltipX}px`,
|
||
|
|
top: `${tooltipY}px`,
|
||
|
|
}"
|
||
|
|
>
|
||
|
|
<div class="tooltip-content">
|
||
|
|
<div><strong>设备:</strong>{{ tooltipData.device }}</div>
|
||
|
|
<div><strong>任务:</strong>{{ tooltipData.task }}</div>
|
||
|
|
<div><strong>时间:</strong>{{ tooltipData.start }} - {{ tooltipData.end }}</div>
|
||
|
|
<div><strong>状态:</strong>{{ tooltipData.status }}</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</basic-container>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
export default {
|
||
|
|
name: 'GanttChart',
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
rowHeight: 36,
|
||
|
|
zoomLevel: 1, // 缩放级别 (1-4)
|
||
|
|
minZoom: 1,
|
||
|
|
maxZoom: 4,
|
||
|
|
|
||
|
|
// 设备列表
|
||
|
|
devices: [
|
||
|
|
'铜合金零件化学镀镍线(9652248)',
|
||
|
|
'铜合金化学镀镍烤箱(9652248-01)',
|
||
|
|
'铜合金化学镀镍烤箱(9652248-02)',
|
||
|
|
'铜合金化学镀镍烤箱(9652248-03)',
|
||
|
|
'铝合金化学镀镍生产线(9653582)',
|
||
|
|
'铝合金化学镀镍烤箱(9653582-01)',
|
||
|
|
'铝合金化学镀镍烤箱(9653582-02)',
|
||
|
|
'铝合金化学镀镍烤箱(9653582-03)',
|
||
|
|
'铝合金化学镀镍烤箱(9653582-04)',
|
||
|
|
'镀金生产线(9652249)',
|
||
|
|
'热表线烘箱(9652249-01)',
|
||
|
|
'热表线烘箱(9652249-02)',
|
||
|
|
'热表线烘箱(9652249-03)',
|
||
|
|
'热表线烘箱(9652249-04)',
|
||
|
|
'喷漆生产线(965396)',
|
||
|
|
'喷码机(9652055)',
|
||
|
|
'喷漆生产线(965396)',
|
||
|
|
'喷码机(9652055)',
|
||
|
|
],
|
||
|
|
|
||
|
|
// 任务数据
|
||
|
|
taskData: [
|
||
|
|
{
|
||
|
|
device: '铜合金零件化学镀镍线(9652248)',
|
||
|
|
task: 'WO-N261026761',
|
||
|
|
start: '00:15',
|
||
|
|
end: '08:45',
|
||
|
|
status: '已完成',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金零件化学镀镍线(9652248)',
|
||
|
|
task: 'WO-N261026762',
|
||
|
|
start: '09:30',
|
||
|
|
end: '12:15',
|
||
|
|
status: '已完成',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金零件化学镀镍线(9652248)',
|
||
|
|
task: 'WO-N261026764',
|
||
|
|
start: '13:20',
|
||
|
|
end: '16:50',
|
||
|
|
status: '已完成',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金零件化学镀镍线(9652248)',
|
||
|
|
task: 'WO-N261026763',
|
||
|
|
start: '16:00',
|
||
|
|
end: '18:30',
|
||
|
|
status: '进行中',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金零件化学镀镍线(9652248)',
|
||
|
|
task: 'WO-N2610287265',
|
||
|
|
start: '19:10',
|
||
|
|
end: '23:45',
|
||
|
|
status: '未开始',
|
||
|
|
},
|
||
|
|
|
||
|
|
{
|
||
|
|
device: '铜合金化学镀镍烤箱(9652248-01)',
|
||
|
|
task: 'WO-N261026727',
|
||
|
|
start: '09:15',
|
||
|
|
end: '11:30',
|
||
|
|
status: '已完成',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金化学镀镍烤箱(9652248-01)',
|
||
|
|
task: 'WO-N261026729',
|
||
|
|
start: '12:20',
|
||
|
|
end: '14:40',
|
||
|
|
status: '已完成',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金化学镀镍烤箱(9652248-01)',
|
||
|
|
task: 'WO-N261026721',
|
||
|
|
start: '15:50',
|
||
|
|
end: '17:20',
|
||
|
|
status: '进行中',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金化学镀镍烤箱(9652248-01)',
|
||
|
|
task: 'WO-N2610287244',
|
||
|
|
start: '18:10',
|
||
|
|
end: '20:30',
|
||
|
|
status: '未开始',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金化学镀镍烤箱(9652248-01)',
|
||
|
|
task: 'WO-N261026778',
|
||
|
|
start: '21:25',
|
||
|
|
end: '23:55',
|
||
|
|
status: '未开始',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金化学镀镍烤箱(9652248-01)',
|
||
|
|
task: 'WO-N2610287244',
|
||
|
|
start: '18:10',
|
||
|
|
end: '20:30',
|
||
|
|
status: '未开始',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
device: '铜合金化学镀镍烤箱(9652248-01)',
|
||
|
|
task: 'WO-N261026778',
|
||
|
|
start: '21:25',
|
||
|
|
end: '23:55',
|
||
|
|
status: '未开始',
|
||
|
|
},
|
||
|
|
],
|
||
|
|
|
||
|
|
// 提示框相关
|
||
|
|
tooltipVisible: false,
|
||
|
|
tooltipData: {},
|
||
|
|
tooltipX: 0,
|
||
|
|
tooltipY: 0,
|
||
|
|
option: {
|
||
|
|
menuSpan: 4,
|
||
|
|
submitBtn: false,
|
||
|
|
emptyBtn: false,
|
||
|
|
menuPosition: 'right',
|
||
|
|
column: [
|
||
|
|
{
|
||
|
|
label: '设备',
|
||
|
|
prop: 'name',
|
||
|
|
span: 5,
|
||
|
|
type: 'select',
|
||
|
|
dicData: [
|
||
|
|
{
|
||
|
|
label: '车间订单',
|
||
|
|
value: 1,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: '设备',
|
||
|
|
value: 2,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: '班组',
|
||
|
|
value: 3,
|
||
|
|
},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: '车间订单号',
|
||
|
|
prop: 'name',
|
||
|
|
span: 5,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: '班组',
|
||
|
|
prop: 'name',
|
||
|
|
span: 5,
|
||
|
|
type: 'select',
|
||
|
|
dicData: [
|
||
|
|
{
|
||
|
|
label: '班组1',
|
||
|
|
value: 1,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: '班组2',
|
||
|
|
value: 2,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: '班组3',
|
||
|
|
value: 3,
|
||
|
|
},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: '时间',
|
||
|
|
prop: 'name',
|
||
|
|
span: 5,
|
||
|
|
type: 'date',
|
||
|
|
},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
};
|
||
|
|
},
|
||
|
|
computed: {
|
||
|
|
// 时间轴总宽度
|
||
|
|
timelineWidth() {
|
||
|
|
return 100 * this.zoomLevel;
|
||
|
|
},
|
||
|
|
// 主刻度标签(小时)
|
||
|
|
majorTickLabels() {
|
||
|
|
return Array.from({ length: 24 }, (_, i) => `${i}:00`);
|
||
|
|
},
|
||
|
|
// 副刻度标签(30分钟)
|
||
|
|
minorTickLabels() {
|
||
|
|
const labels = [];
|
||
|
|
for (let hour = 0; hour < 24; hour++) {
|
||
|
|
labels.push(''); // 小时位置留空(主刻度已显示)
|
||
|
|
labels.push(`${hour}:30`);
|
||
|
|
}
|
||
|
|
return labels;
|
||
|
|
},
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
// 根据设备筛选任务
|
||
|
|
getDeviceTasks(device) {
|
||
|
|
return this.taskData.filter(task => task.device === device);
|
||
|
|
},
|
||
|
|
|
||
|
|
// 时间转分钟数
|
||
|
|
timeToMinutes(timeStr) {
|
||
|
|
const [hours, minutes] = timeStr.split(':').map(Number);
|
||
|
|
return hours * 60 + minutes;
|
||
|
|
},
|
||
|
|
|
||
|
|
// 计算任务起始位置百分比
|
||
|
|
getPositionPercent(startTime) {
|
||
|
|
const totalMinutes = 24 * 60;
|
||
|
|
const startMinutes = this.timeToMinutes(startTime);
|
||
|
|
return (startMinutes / totalMinutes) * 100;
|
||
|
|
},
|
||
|
|
|
||
|
|
// 计算任务宽度百分比
|
||
|
|
getWidthPercent(startTime, endTime) {
|
||
|
|
const startMinutes = this.timeToMinutes(startTime);
|
||
|
|
const endMinutes = this.timeToMinutes(endTime);
|
||
|
|
const duration = endMinutes - startMinutes;
|
||
|
|
return (duration / (24 * 60)) * 100;
|
||
|
|
},
|
||
|
|
|
||
|
|
// 根据状态获取颜色
|
||
|
|
getStatusColor(status) {
|
||
|
|
switch (status) {
|
||
|
|
case '已完成':
|
||
|
|
return '#28a745';
|
||
|
|
case '进行中':
|
||
|
|
return '#007bff';
|
||
|
|
case '未开始':
|
||
|
|
return '#6c757d';
|
||
|
|
default:
|
||
|
|
return '#ccc';
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 鼠标滚轮缩放
|
||
|
|
handleWheel(e) {
|
||
|
|
if (e.deltaY < 0 && this.zoomLevel < this.maxZoom) {
|
||
|
|
this.zoomLevel += 0.5;
|
||
|
|
} else if (e.deltaY > 0 && this.zoomLevel > this.minZoom) {
|
||
|
|
this.zoomLevel -= 0.5;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 放大/缩小/重置
|
||
|
|
zoomIn() {
|
||
|
|
if (this.zoomLevel < this.maxZoom) this.zoomLevel += 0.5;
|
||
|
|
},
|
||
|
|
zoomOut() {
|
||
|
|
if (this.zoomLevel > this.minZoom) this.zoomLevel -= 0.5;
|
||
|
|
},
|
||
|
|
resetZoom() {
|
||
|
|
this.zoomLevel = this.minZoom;
|
||
|
|
},
|
||
|
|
|
||
|
|
// 提示框控制
|
||
|
|
showTooltip(e, task, device) {
|
||
|
|
this.tooltipData = { ...task, device };
|
||
|
|
this.tooltipX = e.pageX + 10;
|
||
|
|
this.tooltipY = e.pageY + 10;
|
||
|
|
this.tooltipVisible = true;
|
||
|
|
},
|
||
|
|
hideTooltip() {
|
||
|
|
this.tooltipVisible = false;
|
||
|
|
},
|
||
|
|
},
|
||
|
|
};
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.gantt-container {
|
||
|
|
width: 100%;
|
||
|
|
padding: 20px;
|
||
|
|
box-sizing: border-box;
|
||
|
|
font-family: Arial, sans-serif;
|
||
|
|
}
|
||
|
|
|
||
|
|
.gantt-header {
|
||
|
|
height: 40px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.zoom-controls {
|
||
|
|
display: flex;
|
||
|
|
gap: 10px;
|
||
|
|
margin-bottom: 15px;
|
||
|
|
padding-left: 265px;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.zoom-btn {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 5px;
|
||
|
|
padding: 4px 10px;
|
||
|
|
background-color: #f1f5f9;
|
||
|
|
border: 1px solid #ddd;
|
||
|
|
border-radius: 4px;
|
||
|
|
cursor: pointer;
|
||
|
|
font-size: 12px;
|
||
|
|
transition: all 0.2s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.zoom-btn:hover {
|
||
|
|
background-color: #e2e8f0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.zoom-info {
|
||
|
|
font-size: 12px;
|
||
|
|
color: #666;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-legend {
|
||
|
|
display: flex;
|
||
|
|
gap: 20px;
|
||
|
|
float: right;
|
||
|
|
}
|
||
|
|
|
||
|
|
.legend-item {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 5px;
|
||
|
|
font-size: 14px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.legend-color {
|
||
|
|
display: inline-block;
|
||
|
|
width: 12px;
|
||
|
|
height: 12px;
|
||
|
|
border-radius: 2px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.legend-color.completed {
|
||
|
|
background-color: #28a745;
|
||
|
|
}
|
||
|
|
.legend-color.processing {
|
||
|
|
background-color: #007bff;
|
||
|
|
}
|
||
|
|
.legend-color.pending {
|
||
|
|
background-color: #6c757d;
|
||
|
|
}
|
||
|
|
|
||
|
|
.gantt-wrapper {
|
||
|
|
display: flex;
|
||
|
|
height: calc(100% - 120px);
|
||
|
|
border: 1px solid #eee;
|
||
|
|
overflow: hidden;
|
||
|
|
}
|
||
|
|
|
||
|
|
.device-list {
|
||
|
|
width: 250px;
|
||
|
|
background-color: #f8f9fa;
|
||
|
|
border-right: 1px solid #eee;
|
||
|
|
/* overflow-y: auto; */
|
||
|
|
flex-shrink: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.device-item {
|
||
|
|
font-size: 16px;
|
||
|
|
color: #333;
|
||
|
|
white-space: nowrap;
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
position: relative;
|
||
|
|
line-height: 36px;
|
||
|
|
padding-left: 15px;
|
||
|
|
padding-right: 15px;
|
||
|
|
&::after {
|
||
|
|
content: '';
|
||
|
|
position: absolute;
|
||
|
|
bottom: 0;
|
||
|
|
left: 0;
|
||
|
|
width: 100%;
|
||
|
|
height: 1px;
|
||
|
|
background-color: #eee;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.device-item-title {
|
||
|
|
background: var(--el-color-primary);
|
||
|
|
text-align: center;
|
||
|
|
color: #fff;
|
||
|
|
}
|
||
|
|
|
||
|
|
.timeline-container {
|
||
|
|
flex: 1;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
overflow: auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 图表X轴区域样式(时间在上,刻度线在下) */
|
||
|
|
.chart-axis {
|
||
|
|
height: 36px;
|
||
|
|
position: relative;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 时间标签容器 */
|
||
|
|
.time-labels {
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
width: 100%;
|
||
|
|
height: 30px;
|
||
|
|
display: flex;
|
||
|
|
background-color: var(--el-color-primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 主刻度标签(小时) */
|
||
|
|
.major-label {
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
/* transform: translateX(-50%); */
|
||
|
|
font-size: 16px;
|
||
|
|
color: #fff;
|
||
|
|
font-weight: 500;
|
||
|
|
white-space: nowrap;
|
||
|
|
line-height: 30px;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 副刻度标签(30分钟) */
|
||
|
|
.minor-labels {
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.minor-label {
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
/* transform: translateX(-50%); */
|
||
|
|
font-size: 16px;
|
||
|
|
color: #fff;
|
||
|
|
white-space: nowrap;
|
||
|
|
line-height: 30px;
|
||
|
|
padding: 0 2px;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 刻度线容器(在下方) */
|
||
|
|
.tick-lines {
|
||
|
|
position: absolute;
|
||
|
|
bottom: 0;
|
||
|
|
left: 0;
|
||
|
|
width: 100%;
|
||
|
|
height: 6px;
|
||
|
|
background-color: var(--el-color-primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 主刻度线(小时) */
|
||
|
|
.major-tick-line {
|
||
|
|
position: absolute;
|
||
|
|
bottom: 0;
|
||
|
|
width: 1px;
|
||
|
|
height: 4px;
|
||
|
|
background-color: #fff;
|
||
|
|
transform: translateX(-50%);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 副刻度线(30分钟) */
|
||
|
|
.minor-tick-lines {
|
||
|
|
position: absolute;
|
||
|
|
bottom: 0;
|
||
|
|
left: 0;
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.minor-tick-line {
|
||
|
|
position: absolute;
|
||
|
|
bottom: 0;
|
||
|
|
width: 1px;
|
||
|
|
height: 4px;
|
||
|
|
background-color: #fff;
|
||
|
|
transform: translateX(-50%);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* X轴基线 */
|
||
|
|
.axis-base-line {
|
||
|
|
position: absolute;
|
||
|
|
bottom: 0;
|
||
|
|
left: 0;
|
||
|
|
width: 100%;
|
||
|
|
height: 1px;
|
||
|
|
background-color: #dee2e6;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 图表内容区域 */
|
||
|
|
.chart-content {
|
||
|
|
flex: 1;
|
||
|
|
position: relative;
|
||
|
|
overflow-y: auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 网格线样式 */
|
||
|
|
.grid-lines {
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
pointer-events: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.grid-line {
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
bottom: 0;
|
||
|
|
width: 0px;
|
||
|
|
background-color: #e9ecef;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 任务容器 */
|
||
|
|
.tasks-container {
|
||
|
|
position: relative;
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.device-task-row {
|
||
|
|
position: relative;
|
||
|
|
border-bottom: 1px solid #e9ecef;
|
||
|
|
box-sizing: border-box;
|
||
|
|
}
|
||
|
|
|
||
|
|
.task-bar {
|
||
|
|
position: absolute;
|
||
|
|
top: 5px;
|
||
|
|
height: 26px;
|
||
|
|
border-radius: 4px;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
padding: 0 10px;
|
||
|
|
box-sizing: border-box;
|
||
|
|
cursor: pointer;
|
||
|
|
overflow: hidden;
|
||
|
|
transition: all 0.2s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.task-bar:hover {
|
||
|
|
transform: translateY(-2px);
|
||
|
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.task-label {
|
||
|
|
font-size: 12px;
|
||
|
|
color: white;
|
||
|
|
white-space: nowrap;
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 提示框 */
|
||
|
|
.tooltip {
|
||
|
|
position: fixed;
|
||
|
|
background-color: white;
|
||
|
|
border: 1px solid #ddd;
|
||
|
|
border-radius: 4px;
|
||
|
|
padding: 10px;
|
||
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||
|
|
z-index: 1000;
|
||
|
|
font-size: 12px;
|
||
|
|
pointer-events: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.tooltip-content div {
|
||
|
|
margin: 3px 0;
|
||
|
|
}
|
||
|
|
</style>
|