|
|
@ -1,12 +1,19 @@ |
|
|
<template> |
|
|
<template> |
|
|
<div class="bottom-timeline" v-if="isVisible"> |
|
|
<div class="bottom-timeline" v-if="isVisible"> |
|
|
<div class="timeline-header"> |
|
|
<div class="timeline-header"> |
|
|
<el-button type="text" size="mini" @click="openSettingsDialog" icon="el-icon-setting" title="时间轴设置"> |
|
|
<div class="timeline-title"> |
|
|
时间轴设置 |
|
|
<i class="el-icon-time"></i> |
|
|
|
|
|
<span>任务时间轴</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="header-actions"> |
|
|
|
|
|
<el-button type="text" size="mini" @click="openSettingsDialog" title="时间轴设置"> |
|
|
|
|
|
<i class="el-icon-setting"></i> |
|
|
|
|
|
<span>设置</span> |
|
|
</el-button> |
|
|
</el-button> |
|
|
<el-button type="text" size="mini" @click="closeTimeline" icon="el-icon-close" title="关闭时间轴"> |
|
|
<el-button type="text" size="mini" @click="closeTimeline" icon="el-icon-close" title="关闭时间轴"> |
|
|
</el-button> |
|
|
</el-button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="timeline-bar-container"> |
|
|
<div class="timeline-bar-container"> |
|
|
<div class="timeline-bar"> |
|
|
<div class="timeline-bar"> |
|
|
@ -22,6 +29,10 @@ |
|
|
:title="segment.time + ' - ' + segment.name" |
|
|
:title="segment.time + ' - ' + segment.name" |
|
|
> |
|
|
> |
|
|
<div class="marker-dot"></div> |
|
|
<div class="marker-dot"></div> |
|
|
|
|
|
<div class="marker-tooltip"> |
|
|
|
|
|
<div class="tooltip-time">{{ segment.time }}</div> |
|
|
|
|
|
<div class="tooltip-name">{{ segment.name }}</div> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="timeline-current" :style="{ left: progressWidth + '%' }"> |
|
|
<div class="timeline-current" :style="{ left: progressWidth + '%' }"> |
|
|
@ -182,8 +193,16 @@ |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<script> |
|
|
<script> |
|
|
|
|
|
import { getTimelineSegmentsByRoomId, addTimelineSegment, updateTimelineSegment, delTimelineSegment } from '@/api/system/timeline' |
|
|
|
|
|
|
|
|
export default { |
|
|
export default { |
|
|
name: 'BottomTimeline', |
|
|
name: 'BottomTimeline', |
|
|
|
|
|
props: { |
|
|
|
|
|
roomId: { |
|
|
|
|
|
type: Number, |
|
|
|
|
|
default: null |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
data() { |
|
|
data() { |
|
|
return { |
|
|
return { |
|
|
isVisible: false, |
|
|
isVisible: false, |
|
|
@ -206,18 +225,29 @@ export default { |
|
|
}, |
|
|
}, |
|
|
timer: null, |
|
|
timer: null, |
|
|
audio: null, |
|
|
audio: null, |
|
|
currentTimeStr: '' |
|
|
currentTimeStr: '', |
|
|
|
|
|
isLoading: false |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
mounted() { |
|
|
mounted() { |
|
|
this.initDefaultTime() |
|
|
this.initDefaultTime() |
|
|
this.initDefaultSegments() |
|
|
this.loadTimelineSegments() |
|
|
this.startTimer() |
|
|
this.startTimer() |
|
|
this.initAudio() |
|
|
this.initAudio() |
|
|
}, |
|
|
}, |
|
|
beforeDestroy() { |
|
|
beforeDestroy() { |
|
|
this.stopTimer() |
|
|
this.stopTimer() |
|
|
}, |
|
|
}, |
|
|
|
|
|
watch: { |
|
|
|
|
|
roomId: { |
|
|
|
|
|
handler(newVal) { |
|
|
|
|
|
if (newVal) { |
|
|
|
|
|
this.loadTimelineSegments() |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
immediate: true |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
methods: { |
|
|
methods: { |
|
|
closeTimeline() { |
|
|
closeTimeline() { |
|
|
this.isVisible = false |
|
|
this.isVisible = false |
|
|
@ -248,6 +278,38 @@ export default { |
|
|
this.editSegment(this.currentSegment) |
|
|
this.editSegment(this.currentSegment) |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
async loadTimelineSegments() { |
|
|
|
|
|
if (!this.roomId) { |
|
|
|
|
|
this.initDefaultSegments() |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
this.isLoading = true |
|
|
|
|
|
const response = await getTimelineSegmentsByRoomId(this.roomId) |
|
|
|
|
|
if (response.code === 200 && response.data && response.data.length > 0) { |
|
|
|
|
|
this.timelineSegments = response.data.map(item => ({ |
|
|
|
|
|
segmentId: item.segmentId, |
|
|
|
|
|
time: item.segmentTime, |
|
|
|
|
|
name: item.segmentName, |
|
|
|
|
|
description: item.segmentDesc, |
|
|
|
|
|
active: false, |
|
|
|
|
|
passed: false, |
|
|
|
|
|
position: 0, |
|
|
|
|
|
triggered: false |
|
|
|
|
|
})) |
|
|
|
|
|
this.updateTimeline() |
|
|
|
|
|
} else { |
|
|
|
|
|
this.initDefaultSegments() |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('加载时间轴数据失败:', error) |
|
|
|
|
|
this.initDefaultSegments() |
|
|
|
|
|
} finally { |
|
|
|
|
|
this.isLoading = false |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
initDefaultTime() { |
|
|
initDefaultTime() { |
|
|
const now = new Date() |
|
|
const now = new Date() |
|
|
this.startTime = new Date(now) |
|
|
this.startTime = new Date(now) |
|
|
@ -433,16 +495,33 @@ export default { |
|
|
this.showSegmentDialog = true |
|
|
this.showSegmentDialog = true |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
saveSegment() { |
|
|
async saveSegment() { |
|
|
if (!this.segmentForm.time || !this.segmentForm.name) { |
|
|
if (!this.segmentForm.time || !this.segmentForm.name) { |
|
|
this.$message.warning('请填写完整信息') |
|
|
this.$message.warning('请填写完整信息') |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const segmentData = { |
|
|
|
|
|
roomId: this.roomId, |
|
|
|
|
|
segmentTime: this.formatTime(this.segmentForm.time), |
|
|
|
|
|
segmentName: this.segmentForm.name, |
|
|
|
|
|
segmentDesc: this.segmentForm.description |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
if (this.isEditMode && this.timelineSegments[this.editIndex].segmentId) { |
|
|
|
|
|
segmentData.segmentId = this.timelineSegments[this.editIndex].segmentId |
|
|
|
|
|
await updateTimelineSegment(segmentData) |
|
|
|
|
|
} else { |
|
|
|
|
|
const response = await addTimelineSegment(segmentData) |
|
|
|
|
|
segmentData.segmentId = response.data |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const segment = { |
|
|
const segment = { |
|
|
time: this.formatTime(this.segmentForm.time), |
|
|
segmentId: segmentData.segmentId, |
|
|
name: this.segmentForm.name, |
|
|
time: segmentData.segmentTime, |
|
|
description: this.segmentForm.description, |
|
|
name: segmentData.segmentName, |
|
|
|
|
|
description: segmentData.segmentDesc, |
|
|
active: false, |
|
|
active: false, |
|
|
passed: false, |
|
|
passed: false, |
|
|
triggered: false, |
|
|
triggered: false, |
|
|
@ -462,17 +541,30 @@ export default { |
|
|
this.updateTimeline() |
|
|
this.updateTimeline() |
|
|
this.showSegmentDialog = false |
|
|
this.showSegmentDialog = false |
|
|
this.$message.success(this.isEditMode ? '编辑成功' : '添加成功') |
|
|
this.$message.success(this.isEditMode ? '编辑成功' : '添加成功') |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('保存时间段失败:', error) |
|
|
|
|
|
this.$message.error('保存失败,请重试') |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
deleteSegment(index) { |
|
|
async deleteSegment(index) { |
|
|
this.$confirm('确定要删除这个时间段吗?', '提示', { |
|
|
this.$confirm('确定要删除这个时间段吗?', '提示', { |
|
|
confirmButtonText: '确定', |
|
|
confirmButtonText: '确定', |
|
|
cancelButtonText: '取消', |
|
|
cancelButtonText: '取消', |
|
|
type: 'warning' |
|
|
type: 'warning' |
|
|
}).then(() => { |
|
|
}).then(async () => { |
|
|
|
|
|
try { |
|
|
|
|
|
const segment = this.timelineSegments[index] |
|
|
|
|
|
if (segment.segmentId && this.roomId) { |
|
|
|
|
|
await delTimelineSegment(segment.segmentId) |
|
|
|
|
|
} |
|
|
this.timelineSegments.splice(index, 1) |
|
|
this.timelineSegments.splice(index, 1) |
|
|
this.updateTimeline() |
|
|
this.updateTimeline() |
|
|
this.$message.success('删除成功') |
|
|
this.$message.success('删除成功') |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('删除时间段失败:', error) |
|
|
|
|
|
this.$message.error('删除失败,请重试') |
|
|
|
|
|
} |
|
|
}).catch(() => {}) |
|
|
}).catch(() => {}) |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
@ -509,49 +601,89 @@ export default { |
|
|
left: 0; |
|
|
left: 0; |
|
|
right: 0; |
|
|
right: 0; |
|
|
z-index: 1000; |
|
|
z-index: 1000; |
|
|
background: rgba(255, 255, 255, 0.95); |
|
|
background: linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%); |
|
|
backdrop-filter: blur(10px); |
|
|
backdrop-filter: blur(12px); |
|
|
border-top: 1px solid #e4e7ed; |
|
|
border-top: 1px solid rgba(255, 255, 255, 0.8); |
|
|
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08); |
|
|
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.08); |
|
|
padding: 4px 12px 6px 12px; |
|
|
padding: 2px 20px 4px; |
|
|
|
|
|
border-radius: 12px 12px 0 0; |
|
|
|
|
|
overflow: visible; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-header { |
|
|
.timeline-header { |
|
|
display: flex; |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
height: 16px; |
|
|
min-height: 24px; |
|
|
margin-bottom: 3px; |
|
|
margin-bottom: 2px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.timeline-title { |
|
|
|
|
|
font-size: 15px; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
color: #1e293b; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
gap: 8px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.timeline-title i { |
|
|
|
|
|
color: #008aff; |
|
|
|
|
|
font-size: 16px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.header-actions { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
gap: 8px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.header-actions .el-button { |
|
|
|
|
|
color: #008aff; |
|
|
|
|
|
transition: all 0.3s ease; |
|
|
|
|
|
font-size: 15px; |
|
|
|
|
|
padding: 4px 8px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.header-actions .el-button:hover { |
|
|
|
|
|
color: #0078e5; |
|
|
|
|
|
transform: translateY(-1px); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-bar { |
|
|
.timeline-bar { |
|
|
position: relative; |
|
|
position: relative; |
|
|
height: 8px; |
|
|
height: 14px; |
|
|
background: #f5f7fa; |
|
|
background: rgba(255, 255, 255, 0.6); |
|
|
border-radius: 4px; |
|
|
border-radius: 8px; |
|
|
overflow: hidden; |
|
|
overflow: visible; |
|
|
border: 1px solid #e4e7ed; |
|
|
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-bar-container { |
|
|
.timeline-bar-container { |
|
|
position: relative; |
|
|
position: relative; |
|
|
margin-bottom: 0; |
|
|
margin-bottom: 2px; |
|
|
|
|
|
overflow: visible; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-scale { |
|
|
.timeline-scale { |
|
|
position: relative; |
|
|
position: relative; |
|
|
height: 14px; |
|
|
height: 20px; |
|
|
margin-top: 3px; |
|
|
margin-top: 2px; |
|
|
display: flex; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.scale-label { |
|
|
.scale-label { |
|
|
position: absolute; |
|
|
position: absolute; |
|
|
font-size: 12px; |
|
|
font-size: 13px; |
|
|
color: #909399; |
|
|
color: #64748b; |
|
|
transform: translateX(-50%); |
|
|
transform: translateX(-50%); |
|
|
white-space: nowrap; |
|
|
white-space: nowrap; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
padding: 3px 8px; |
|
|
|
|
|
background: rgba(255, 255, 255, 0.9); |
|
|
|
|
|
border-radius: 6px; |
|
|
|
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-progress { |
|
|
.timeline-progress { |
|
|
@ -559,9 +691,10 @@ export default { |
|
|
top: 0; |
|
|
top: 0; |
|
|
left: 0; |
|
|
left: 0; |
|
|
height: 100%; |
|
|
height: 100%; |
|
|
background: linear-gradient(90deg, #409EFF 0%, #67c23a 100%); |
|
|
background: linear-gradient(90deg, #008aff 0%, #66b8ff 100%); |
|
|
transition: width 0.5s ease; |
|
|
transition: width 0.5s ease-in-out; |
|
|
border-radius: 6px; |
|
|
border-radius: 8px; |
|
|
|
|
|
box-shadow: 0 0 8px rgba(0, 138, 255, 0.3); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-markers { |
|
|
.timeline-markers { |
|
|
@ -574,102 +707,132 @@ export default { |
|
|
|
|
|
|
|
|
.timeline-marker { |
|
|
.timeline-marker { |
|
|
position: absolute; |
|
|
position: absolute; |
|
|
top: 0; |
|
|
top: 50%; |
|
|
transform: translateX(-50%); |
|
|
transform: translate(-50%, -50%); |
|
|
cursor: pointer; |
|
|
cursor: pointer; |
|
|
z-index: 10; |
|
|
z-index: 10; |
|
|
height: 100%; |
|
|
transition: all 0.3s ease; |
|
|
display: flex; |
|
|
} |
|
|
align-items: center; |
|
|
|
|
|
|
|
|
.timeline-marker:hover { |
|
|
|
|
|
transform: translate(-50%, -50%) translateY(-3px); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-marker:hover .marker-dot { |
|
|
.timeline-marker:hover .marker-dot { |
|
|
transform: scale(1.3); |
|
|
transform: scale(1.5); |
|
|
box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.3); |
|
|
box-shadow: 0 0 0 6px rgba(0, 138, 255, 0.2); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.timeline-marker:hover .marker-tooltip { |
|
|
|
|
|
opacity: 1; |
|
|
|
|
|
visibility: visible; |
|
|
|
|
|
transform: translateX(-50%) translateY(-12px); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-marker.active .marker-dot { |
|
|
.timeline-marker.active .marker-dot { |
|
|
background: #E6A23C; |
|
|
background: #f59e0b; |
|
|
box-shadow: 0 0 0 3px rgba(230, 162, 60, 0.4); |
|
|
box-shadow: 0 0 0 6px rgba(245, 158, 11, 0.3); |
|
|
animation: pulse 1.5s infinite; |
|
|
animation: pulse 2s infinite ease-in-out; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-marker.passed .marker-dot { |
|
|
.timeline-marker.passed .marker-dot { |
|
|
background: #67c23a; |
|
|
background: #10b981; |
|
|
box-shadow: 0 0 0 2px rgba(103, 194, 58, 0.3); |
|
|
box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.2); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.marker-dot { |
|
|
.marker-dot { |
|
|
width: 6px; |
|
|
width: 12px; |
|
|
height: 6px; |
|
|
height: 12px; |
|
|
background: #409EFF; |
|
|
background: #008aff; |
|
|
border-radius: 50%; |
|
|
border-radius: 50%; |
|
|
border: 1px solid white; |
|
|
border: 2px solid white; |
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); |
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); |
|
|
transition: all 0.3s; |
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
|
|
|
|
|
z-index: 1; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.marker-tooltip { |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
bottom: 100%; |
|
|
|
|
|
left: 50%; |
|
|
|
|
|
transform: translateX(-50%) translateY(0); |
|
|
|
|
|
background: white; |
|
|
|
|
|
border-radius: 8px; |
|
|
|
|
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12); |
|
|
|
|
|
padding: 8px 12px; |
|
|
|
|
|
min-width: 140px; |
|
|
|
|
|
opacity: 0; |
|
|
|
|
|
visibility: hidden; |
|
|
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
|
|
|
|
|
z-index: 20; |
|
|
|
|
|
border: 1px solid #e2e8f0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.tooltip-time { |
|
|
|
|
|
font-size: 16px; |
|
|
|
|
|
color: #008aff; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
margin-bottom: 4px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.tooltip-name { |
|
|
|
|
|
font-size: 15px; |
|
|
|
|
|
color: #1e293b; |
|
|
|
|
|
font-weight: 500; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-current { |
|
|
.timeline-current { |
|
|
position: absolute; |
|
|
position: absolute; |
|
|
top: 0; |
|
|
top: 0; |
|
|
width: 2px; |
|
|
width: 4px; |
|
|
height: 100%; |
|
|
height: 100%; |
|
|
background: #F56C6C; |
|
|
background: #ef4444; |
|
|
transform: translateX(-50%); |
|
|
transform: translateX(-50%); |
|
|
z-index: 20; |
|
|
z-index: 20; |
|
|
transition: left 0.5s ease; |
|
|
transition: left 0.5s ease-in-out; |
|
|
|
|
|
border-radius: 4px; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.current-indicator { |
|
|
.current-indicator { |
|
|
position: absolute; |
|
|
position: absolute; |
|
|
top: 0; |
|
|
top: 50%; |
|
|
left: -4px; |
|
|
left: -6px; |
|
|
width: 8px; |
|
|
width: 14px; |
|
|
height: 8px; |
|
|
height: 14px; |
|
|
background: #F56C6C; |
|
|
background: #ef4444; |
|
|
border-radius: 50%; |
|
|
border-radius: 50%; |
|
|
border: 1px solid white; |
|
|
border: 2px solid white; |
|
|
box-shadow: 0 1px 3px rgba(245, 108, 108, 0.4); |
|
|
box-shadow: 0 0 12px rgba(239, 68, 68, 0.5); |
|
|
animation: currentPulse 2s infinite; |
|
|
transform: translateY(-50%); |
|
|
|
|
|
animation: currentPulse 1.5s infinite ease-in-out; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.current-time { |
|
|
.current-time { |
|
|
position: absolute; |
|
|
position: absolute; |
|
|
top: -14px; |
|
|
top: -26px; |
|
|
left: 0; |
|
|
left: 50%; |
|
|
transform: translateX(-50%); |
|
|
transform: translateX(-50%); |
|
|
font-size: 12px; |
|
|
font-size: 13px; |
|
|
color: #F56C6C; |
|
|
color: #ef4444; |
|
|
white-space: nowrap; |
|
|
white-space: nowrap; |
|
|
font-weight: 500; |
|
|
font-weight: 600; |
|
|
z-index: 25; |
|
|
z-index: 25; |
|
|
|
|
|
padding: 4px 10px; |
|
|
|
|
|
background: white; |
|
|
|
|
|
border-radius: 8px; |
|
|
|
|
|
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@keyframes pulse { |
|
|
@keyframes pulse { |
|
|
0% { |
|
|
0% { transform: scale(1); opacity: 1; } |
|
|
transform: scale(1); |
|
|
50% { transform: scale(1.4); opacity: 0.8; } |
|
|
opacity: 1; |
|
|
100% { transform: scale(1); opacity: 1; } |
|
|
} |
|
|
|
|
|
50% { |
|
|
|
|
|
transform: scale(1.2); |
|
|
|
|
|
opacity: 0.8; |
|
|
|
|
|
} |
|
|
|
|
|
100% { |
|
|
|
|
|
transform: scale(1); |
|
|
|
|
|
opacity: 1; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@keyframes currentPulse { |
|
|
@keyframes currentPulse { |
|
|
0% { |
|
|
0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); } |
|
|
box-shadow: 0 1px 3px rgba(245, 108, 108, 0.4); |
|
|
70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); } |
|
|
} |
|
|
100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } |
|
|
50% { |
|
|
|
|
|
box-shadow: 0 1px 6px rgba(245, 108, 108, 0.6); |
|
|
|
|
|
} |
|
|
|
|
|
100% { |
|
|
|
|
|
box-shadow: 0 1px 3px rgba(245, 108, 108, 0.4); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.settings-content { |
|
|
.settings-content { |
|
|
@ -679,8 +842,9 @@ export default { |
|
|
.time-settings { |
|
|
.time-settings { |
|
|
margin-bottom: 20px; |
|
|
margin-bottom: 20px; |
|
|
padding: 15px; |
|
|
padding: 15px; |
|
|
background: #f5f7fa; |
|
|
background: #f8fafc; |
|
|
border-radius: 6px; |
|
|
border-radius: 8px; |
|
|
|
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.setting-row { |
|
|
.setting-row { |
|
|
@ -698,7 +862,7 @@ export default { |
|
|
|
|
|
|
|
|
.setting-label { |
|
|
.setting-label { |
|
|
font-size: 13px; |
|
|
font-size: 13px; |
|
|
color: #606266; |
|
|
color: #334155; |
|
|
font-weight: 500; |
|
|
font-weight: 500; |
|
|
white-space: nowrap; |
|
|
white-space: nowrap; |
|
|
min-width: 60px; |
|
|
min-width: 60px; |
|
|
@ -714,13 +878,13 @@ export default { |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
margin-bottom: 15px; |
|
|
margin-bottom: 15px; |
|
|
padding-bottom: 10px; |
|
|
padding-bottom: 10px; |
|
|
border-bottom: 1px solid #dcdfe6; |
|
|
border-bottom: 1px solid #e2e8f0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.section-header h4 { |
|
|
.section-header h4 { |
|
|
margin: 0; |
|
|
margin: 0; |
|
|
font-size: 14px; |
|
|
font-size: 14px; |
|
|
color: #303133; |
|
|
color: #1e293b; |
|
|
font-weight: 600; |
|
|
font-weight: 600; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -732,31 +896,32 @@ export default { |
|
|
.segment-item { |
|
|
.segment-item { |
|
|
display: flex; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
padding: 12px; |
|
|
padding: 12px 16px; |
|
|
background: white; |
|
|
background: white; |
|
|
border: 1px solid #dcdfe6; |
|
|
border: 1px solid #e2e8f0; |
|
|
border-radius: 6px; |
|
|
border-radius: 8px; |
|
|
margin-bottom: 10px; |
|
|
margin-bottom: 8px; |
|
|
transition: all 0.3s; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.segment-item:hover { |
|
|
.segment-item:hover { |
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.08); |
|
|
|
|
|
transform: translateY(-1px); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.segment-item.active { |
|
|
.segment-item.active { |
|
|
background: linear-gradient(135deg, rgba(230, 162, 60, 0.05) 0%, rgba(230, 162, 60, 0.1) 100%); |
|
|
background: linear-gradient(135deg, rgba(245,158,11,0.05) 0%, rgba(245,158,11,0.1) 100%); |
|
|
border-color: #E6A23C; |
|
|
border-color: #f59e0b; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.segment-item.passed { |
|
|
.segment-item.passed { |
|
|
background: linear-gradient(135deg, rgba(103, 194, 58, 0.05) 0%, rgba(103, 194, 58, 0.1) 100%); |
|
|
background: linear-gradient(135deg, rgba(16,185,129,0.05) 0%, rgba(16,185,129,0.1) 100%); |
|
|
border-color: #67c23a; |
|
|
border-color: #10b981; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.segment-time { |
|
|
.segment-time { |
|
|
font-size: 13px; |
|
|
font-size: 13px; |
|
|
color: #409EFF; |
|
|
color: #008aff; |
|
|
font-weight: 700; |
|
|
font-weight: 700; |
|
|
min-width: 80px; |
|
|
min-width: 80px; |
|
|
} |
|
|
} |
|
|
@ -769,13 +934,13 @@ export default { |
|
|
.segment-name { |
|
|
.segment-name { |
|
|
font-size: 14px; |
|
|
font-size: 14px; |
|
|
font-weight: 600; |
|
|
font-weight: 600; |
|
|
color: #303133; |
|
|
color: #1e293b; |
|
|
margin-bottom: 3px; |
|
|
margin-bottom: 3px; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.segment-desc { |
|
|
.segment-desc { |
|
|
font-size: 12px; |
|
|
font-size: 12px; |
|
|
color: #909399; |
|
|
color: #64748b; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.segment-status { |
|
|
.segment-status { |
|
|
@ -799,13 +964,13 @@ export default { |
|
|
|
|
|
|
|
|
.detail-row label { |
|
|
.detail-row label { |
|
|
font-weight: 600; |
|
|
font-weight: 600; |
|
|
color: #303133; |
|
|
color: #1e293b; |
|
|
min-width: 80px; |
|
|
min-width: 80px; |
|
|
flex-shrink: 0; |
|
|
flex-shrink: 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.detail-row span { |
|
|
.detail-row span { |
|
|
color: #606266; |
|
|
color: #334155; |
|
|
line-height: 1.5; |
|
|
line-height: 1.5; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -823,35 +988,49 @@ export default { |
|
|
left: 50% !important; |
|
|
left: 50% !important; |
|
|
transform: translate(-50%, -50%) !important; |
|
|
transform: translate(-50%, -50%) !important; |
|
|
margin: 0 !important; |
|
|
margin: 0 !important; |
|
|
|
|
|
border-radius: 12px; |
|
|
|
|
|
overflow: hidden; |
|
|
|
|
|
box-shadow: 0 10px 25px rgba(0,0,0,0.1); |
|
|
|
|
|
border: none; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.bottom-timeline .el-dialog__header { |
|
|
.bottom-timeline .el-dialog__header { |
|
|
background: #409EFF; |
|
|
background: linear-gradient(90deg, #008aff 0%, #66b8ff 100%); |
|
|
color: white; |
|
|
color: white; |
|
|
|
|
|
padding: 12px 20px; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.bottom-timeline .el-dialog__title { |
|
|
.bottom-timeline .el-dialog__title { |
|
|
color: white; |
|
|
color: white; |
|
|
font-size: 14px; |
|
|
font-size: 15px; |
|
|
font-weight: 600; |
|
|
font-weight: 600; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.bottom-timeline .el-dialog__headerbtn .el-dialog__close { |
|
|
.bottom-timeline .el-dialog__headerbtn .el-dialog__close { |
|
|
color: white; |
|
|
color: white; |
|
|
|
|
|
font-size: 16px; |
|
|
|
|
|
transition: all 0.3s ease; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.bottom-timeline .el-dialog__headerbtn .el-dialog__close:hover { |
|
|
|
|
|
color: #e0e7ff; |
|
|
|
|
|
transform: rotate(90deg); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.bottom-timeline .el-dialog__body { |
|
|
.bottom-timeline .el-dialog__body { |
|
|
padding: 20px; |
|
|
padding: 24px; |
|
|
|
|
|
background: #f8fafc; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.bottom-timeline .el-dialog__footer { |
|
|
.bottom-timeline .el-dialog__footer { |
|
|
padding: 15px 20px; |
|
|
padding: 16px 24px; |
|
|
border-top: 1px solid #dcdfe6; |
|
|
border-top: 1px solid #e2e8f0; |
|
|
|
|
|
background: white; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-settings-dialog.el-dialog { |
|
|
.timeline-settings-dialog.el-dialog { |
|
|
position: fixed !important; |
|
|
position: fixed !important; |
|
|
bottom: 60px !important; |
|
|
bottom: 130px !important; |
|
|
top: auto !important; |
|
|
top: auto !important; |
|
|
left: 50% !important; |
|
|
left: 50% !important; |
|
|
transform: translateX(-50%) !important; |
|
|
transform: translateX(-50%) !important; |
|
|
@ -861,7 +1040,7 @@ export default { |
|
|
|
|
|
|
|
|
.timeline-segment-dialog.el-dialog { |
|
|
.timeline-segment-dialog.el-dialog { |
|
|
position: fixed !important; |
|
|
position: fixed !important; |
|
|
bottom: 60px !important; |
|
|
bottom: 130px !important; |
|
|
top: auto !important; |
|
|
top: auto !important; |
|
|
left: 50% !important; |
|
|
left: 50% !important; |
|
|
transform: translateX(-50%) !important; |
|
|
transform: translateX(-50%) !important; |
|
|
@ -871,7 +1050,7 @@ export default { |
|
|
|
|
|
|
|
|
.timeline-detail-dialog.el-dialog { |
|
|
.timeline-detail-dialog.el-dialog { |
|
|
position: fixed !important; |
|
|
position: fixed !important; |
|
|
bottom: 60px !important; |
|
|
bottom: 130px !important; |
|
|
top: auto !important; |
|
|
top: auto !important; |
|
|
left: 50% !important; |
|
|
left: 50% !important; |
|
|
transform: translateX(-50%) !important; |
|
|
transform: translateX(-50%) !important; |
|
|
|