6 changed files with 1072 additions and 582 deletions
@ -0,0 +1,128 @@ |
|||
<template> |
|||
<div class="drawing-toolbar" v-if="drawDomClick"> |
|||
<div class="toolbar-icons"> |
|||
<div |
|||
v-for="item in toolbarItems" |
|||
:key="item.id" |
|||
class="toolbar-item" |
|||
:class="{ active: drawingMode === item.id }" |
|||
@click="handleItemClick(item)" |
|||
:title="item.name" |
|||
> |
|||
<i :class="item.icon"></i> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'DrawingToolbar', |
|||
props: { |
|||
drawDomClick: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
drawingMode: { |
|||
type: String, |
|||
default: null |
|||
}, |
|||
hasEntities: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
toolbarItems: [ |
|||
{ id: 'point', name: '点', icon: 'el-icon-location' }, |
|||
{ id: 'line', name: '线', icon: 'el-icon-edit-outline' }, |
|||
{ id: 'polygon', name: '面', icon: 'el-icon-s-grid' }, |
|||
{ id: 'rectangle', name: '矩形', icon: 'el-icon-s-data' }, |
|||
{ id: 'circle', name: '圆形', icon: 'el-icon-circle-plus-outline' }, |
|||
{ id: 'locate', name: '定位', icon: 'el-icon-aim' }, |
|||
{ id: 'clear', name: '清除', icon: 'el-icon-delete' }, |
|||
{ id: 'import', name: '导入', icon: 'el-icon-upload' }, |
|||
{ id: 'export', name: '导出', icon: 'el-icon-download' } |
|||
] |
|||
} |
|||
}, |
|||
methods: { |
|||
handleItemClick(item) { |
|||
if (item.id === 'clear') { |
|||
this.$emit('clear-all') |
|||
} else if (item.id === 'export') { |
|||
this.$emit('export-data') |
|||
} else if (item.id === 'import') { |
|||
this.$emit('import-data') |
|||
} else if (item.id === 'locate') { |
|||
this.$emit('locate') |
|||
} else { |
|||
this.$emit('toggle-drawing', item.id) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.drawing-toolbar { |
|||
position: absolute; |
|||
top: 70px; |
|||
right: 20px; |
|||
width: 40px; |
|||
background: rgba(255, 255, 255, 0.3); |
|||
backdrop-filter: blur(10px); |
|||
border: 1px solid rgba(0, 138, 255, 0.1); |
|||
border-radius: 8px; |
|||
z-index: 90; |
|||
box-shadow: 0 4px 12px rgba(0, 138, 255, 0.2); |
|||
padding: 15px 5px; |
|||
transition: all 0.3s ease; |
|||
overflow: hidden; |
|||
opacity: 1; |
|||
transform: translateX(0); |
|||
} |
|||
|
|||
.toolbar-icons { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10px; |
|||
margin-top: 30px; |
|||
} |
|||
|
|||
.toolbar-item { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 40px; |
|||
cursor: pointer; |
|||
color: #555; |
|||
font-size: 20px; |
|||
position: relative; |
|||
transition: all 0.3s; |
|||
border-radius: 4px; |
|||
padding: 0 5px; |
|||
background: rgba(255, 255, 255, 0.8); |
|||
backdrop-filter: blur(5px); |
|||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
.toolbar-item:hover { |
|||
background: rgba(0, 138, 255, 0.1); |
|||
color: #008aff; |
|||
transform: translateY(-2px); |
|||
box-shadow: 0 4px 10px rgba(0, 138, 255, 0.2); |
|||
} |
|||
|
|||
.toolbar-item.active { |
|||
background: rgba(0, 138, 255, 0.15); |
|||
color: #008aff; |
|||
box-shadow: 0 2px 8px rgba(0, 138, 255, 0.3); |
|||
} |
|||
|
|||
.toolbar-item:disabled { |
|||
opacity: 0.5; |
|||
cursor: not-allowed; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,104 @@ |
|||
<template> |
|||
<div class="measurement-panel"> |
|||
<div class="measurement-content"> |
|||
<h5>测量结果</h5> |
|||
<div class="measurement-item" v-if="result.distance"> |
|||
<span>长度:</span> |
|||
<strong>{{ result.distance.toFixed(2) }} 米</strong> |
|||
</div> |
|||
<div class="measurement-item" v-if="result.area"> |
|||
<span>面积:</span> |
|||
<strong>{{ result.area.toFixed(2) }} 平方米</strong> |
|||
</div> |
|||
<div class="measurement-item" v-if="result.radius"> |
|||
<span>半径:</span> |
|||
<strong>{{ result.radius.toFixed(2) }} 米</strong> |
|||
</div> |
|||
<button @click="handleClose" class="close-btn">关闭</button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'MeasurementPanel', |
|||
props: { |
|||
result: { |
|||
type: Object, |
|||
default: null |
|||
} |
|||
}, |
|||
methods: { |
|||
handleClose() { |
|||
this.$emit('close') |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.measurement-panel { |
|||
position: absolute; |
|||
bottom: 20px; |
|||
right: 80px; |
|||
z-index: 85; |
|||
background: rgba(255, 255, 255, 0.95); |
|||
backdrop-filter: blur(10px); |
|||
border-radius: 12px; |
|||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); |
|||
padding: 20px; |
|||
min-width: 250px; |
|||
} |
|||
|
|||
.measurement-content h5 { |
|||
margin: 0 0 15px 0; |
|||
font-size: 16px; |
|||
font-weight: 600; |
|||
color: #333; |
|||
border-bottom: 2px solid #409EFF; |
|||
padding-bottom: 8px; |
|||
} |
|||
|
|||
.measurement-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 12px; |
|||
font-size: 14px; |
|||
color: #666; |
|||
} |
|||
|
|||
.measurement-item strong { |
|||
color: #409EFF; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.close-btn { |
|||
width: 100%; |
|||
padding: 10px; |
|||
background: #409EFF; |
|||
color: white; |
|||
border: none; |
|||
border-radius: 6px; |
|||
cursor: pointer; |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
transition: all 0.3s; |
|||
margin-top: 15px; |
|||
} |
|||
|
|||
.close-btn:hover { |
|||
background: #66b1ff; |
|||
transform: translateY(-2px); |
|||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3); |
|||
} |
|||
|
|||
@media (max-width: 768px) { |
|||
.measurement-panel { |
|||
bottom: 10px; |
|||
right: 10px; |
|||
left: 10px; |
|||
min-width: auto; |
|||
} |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,391 @@ |
|||
<template> |
|||
<div class="bottom-left-panel"> |
|||
<div class="panel-toggle" @click="togglePanel" :title="isExpanded ? '收起' : '展开'"> |
|||
<i :class="isExpanded ? 'el-icon-s-fold' : 'el-icon-s-unfold'"></i> |
|||
<span v-if="!isExpanded" class="toggle-text">工具</span> |
|||
</div> |
|||
|
|||
<div class="panel-content" :class="{ expanded: isExpanded }"> |
|||
<div class="panel-item" @click="showTimeline"> |
|||
<i class="el-icon-time"></i> |
|||
<span>时间线</span> |
|||
</div> |
|||
<div class="panel-item" @click="showProgress"> |
|||
<i class="el-icon-s-data"></i> |
|||
<span>进度检查</span> |
|||
</div> |
|||
<div class="panel-item" @click="showSixSteps"> |
|||
<i class="el-icon-s-operation"></i> |
|||
<span>六步法</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<el-dialog |
|||
:visible.sync="dialogVisible" |
|||
:title="dialogTitle" |
|||
width="350px" |
|||
:modal="false" |
|||
custom-class="panel-dialog" |
|||
> |
|||
<div class="dialog-content"> |
|||
<div v-if="activeTab === 'timeline'" class="timeline-content"> |
|||
<h3>时间线</h3> |
|||
<div class="timeline-list"> |
|||
<div v-for="(item, index) in timelineData" :key="index" class="timeline-item"> |
|||
<div class="timeline-dot"></div> |
|||
<div class="timeline-info"> |
|||
<div class="timeline-time">{{ item.time }}</div> |
|||
<div class="timeline-event">{{ item.event }}</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div v-if="activeTab === 'progress'" class="progress-content"> |
|||
<h3>进度检查</h3> |
|||
<div class="progress-list"> |
|||
<div v-for="(item, index) in progressData" :key="index" class="progress-item"> |
|||
<div class="progress-label">{{ item.label }}</div> |
|||
<el-progress :percentage="item.percentage" :status="item.status"></el-progress> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div v-if="activeTab === 'sixsteps'" class="sixsteps-content"> |
|||
<h3>六步法</h3> |
|||
<div class="steps-container"> |
|||
<div v-for="(step, index) in sixStepsData" :key="index" class="step-item" :class="{ active: step.active, completed: step.completed }"> |
|||
<div class="step-number">{{ index + 1 }}</div> |
|||
<div class="step-content"> |
|||
<div class="step-title">{{ step.title }}</div> |
|||
<div class="step-desc">{{ step.desc }}</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'BottomLeftPanel', |
|||
data() { |
|||
return { |
|||
isExpanded: false, |
|||
dialogVisible: false, |
|||
dialogTitle: '', |
|||
activeTab: '', |
|||
timelineData: [ |
|||
{ time: 'K-02:00', event: '任务准备阶段' }, |
|||
{ time: 'K-01:00', event: '资源调配' }, |
|||
{ time: 'K时', event: '任务执行' }, |
|||
{ time: 'K+01:00', event: '任务监控' }, |
|||
{ time: 'K+02:00', event: '任务完成' } |
|||
], |
|||
progressData: [ |
|||
{ label: '任务准备', percentage: 100, status: 'success' }, |
|||
{ label: '资源调配', percentage: 80, status: '' }, |
|||
{ label: '任务执行', percentage: 45, status: '' }, |
|||
{ label: '任务监控', percentage: 20, status: 'exception' }, |
|||
{ label: '任务完成', percentage: 0, status: '' } |
|||
], |
|||
sixStepsData: [ |
|||
{ title: '理解', desc: '明确任务目标和要求', active: true, completed: true }, |
|||
{ title: '判断', desc: '评估可用资源和能力', active: false, completed: true }, |
|||
{ title: '规划', desc: '制定详细执行方案', active: false, completed: false }, |
|||
{ title: '准备', desc: '识别和评估潜在风险', active: false, completed: false }, |
|||
{ title: '执行', desc: '实时监控执行过程', active: false, completed: false }, |
|||
{ title: '评估', desc: '评估任务完成效果', active: false, completed: false } |
|||
] |
|||
} |
|||
}, |
|||
methods: { |
|||
togglePanel() { |
|||
this.isExpanded = !this.isExpanded |
|||
}, |
|||
showTimeline() { |
|||
this.activeTab = 'timeline' |
|||
this.dialogTitle = '时间线' |
|||
this.dialogVisible = true |
|||
}, |
|||
showProgress() { |
|||
this.activeTab = 'progress' |
|||
this.dialogTitle = '进度检查' |
|||
this.dialogVisible = true |
|||
}, |
|||
showSixSteps() { |
|||
this.activeTab = 'sixsteps' |
|||
this.dialogTitle = '六步法' |
|||
this.dialogVisible = true |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.bottom-left-panel { |
|||
position: absolute; |
|||
bottom: 20px; |
|||
left: 20px; |
|||
z-index: 100; |
|||
} |
|||
|
|||
.panel-toggle { |
|||
width: 50px; |
|||
height: 50px; |
|||
background: rgba(0, 138, 255, 0.9); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
cursor: pointer; |
|||
color: white; |
|||
font-size: 20px; |
|||
box-shadow: 0 4px 12px rgba(0, 138, 255, 0.4); |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
.panel-toggle:hover { |
|||
transform: scale(1.1); |
|||
box-shadow: 0 6px 16px rgba(0, 138, 255, 0.6); |
|||
} |
|||
|
|||
.toggle-text { |
|||
font-size: 10px; |
|||
margin-top: 2px; |
|||
} |
|||
|
|||
.panel-content { |
|||
position: absolute; |
|||
bottom: 60px; |
|||
left: 0; |
|||
background: rgba(255, 255, 255, 0.95); |
|||
backdrop-filter: blur(10px); |
|||
border-radius: 12px; |
|||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); |
|||
padding: 10px; |
|||
min-width: 150px; |
|||
opacity: 0; |
|||
transform: translateY(20px); |
|||
pointer-events: none; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
.panel-content.expanded { |
|||
opacity: 1; |
|||
transform: translateY(0); |
|||
pointer-events: auto; |
|||
} |
|||
|
|||
.panel-item { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 12px 15px; |
|||
cursor: pointer; |
|||
border-radius: 8px; |
|||
transition: all 0.3s; |
|||
color: #333; |
|||
} |
|||
|
|||
.panel-item:hover { |
|||
background: rgba(0, 138, 255, 0.1); |
|||
color: #008aff; |
|||
transform: translateX(5px); |
|||
} |
|||
|
|||
.panel-item i { |
|||
font-size: 18px; |
|||
margin-right: 10px; |
|||
color: #008aff; |
|||
} |
|||
|
|||
.panel-item span { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.dialog-content { |
|||
padding: 0; |
|||
} |
|||
|
|||
.timeline-content h3, |
|||
.progress-content h3, |
|||
.sixsteps-content h3 { |
|||
margin: 0 0 15px 0; |
|||
font-size: 14px; |
|||
color: #333; |
|||
border-bottom: 2px solid #409EFF; |
|||
padding-bottom: 8px; |
|||
} |
|||
|
|||
.timeline-list { |
|||
max-height: 280px; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.timeline-item { |
|||
display: flex; |
|||
align-items: flex-start; |
|||
margin-bottom: 12px; |
|||
position: relative; |
|||
} |
|||
|
|||
.timeline-item::before { |
|||
content: ''; |
|||
position: absolute; |
|||
left: 5px; |
|||
top: 15px; |
|||
bottom: -12px; |
|||
width: 2px; |
|||
background: #e0e0e0; |
|||
} |
|||
|
|||
.timeline-item:last-child::before { |
|||
display: none; |
|||
} |
|||
|
|||
.timeline-dot { |
|||
width: 12px; |
|||
height: 12px; |
|||
background: #409EFF; |
|||
border-radius: 50%; |
|||
margin-right: 10px; |
|||
flex-shrink: 0; |
|||
box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.2); |
|||
} |
|||
|
|||
.timeline-info { |
|||
flex: 1; |
|||
} |
|||
|
|||
.timeline-time { |
|||
font-size: 12px; |
|||
color: #409EFF; |
|||
font-weight: 600; |
|||
margin-bottom: 3px; |
|||
} |
|||
|
|||
.timeline-event { |
|||
font-size: 12px; |
|||
color: #666; |
|||
} |
|||
|
|||
.progress-list { |
|||
max-height: 280px; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.progress-item { |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.progress-label { |
|||
font-size: 12px; |
|||
color: #333; |
|||
margin-bottom: 5px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.steps-container { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 8px; |
|||
} |
|||
|
|||
.step-item { |
|||
display: flex; |
|||
align-items: flex-start; |
|||
padding: 10px; |
|||
background: #f5f7fa; |
|||
border-radius: 6px; |
|||
transition: all 0.3s; |
|||
border-left: 3px solid #dcdfe6; |
|||
} |
|||
|
|||
.step-item.active { |
|||
background: rgba(64, 158, 255, 0.1); |
|||
border-left-color: #409EFF; |
|||
} |
|||
|
|||
.step-item.completed { |
|||
background: rgba(103, 194, 58, 0.1); |
|||
border-left-color: #67c23a; |
|||
} |
|||
|
|||
.step-number { |
|||
width: 24px; |
|||
height: 24px; |
|||
background: #dcdfe6; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
font-size: 12px; |
|||
font-weight: 600; |
|||
color: #909399; |
|||
margin-right: 10px; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.step-item.active .step-number { |
|||
background: #409EFF; |
|||
color: white; |
|||
} |
|||
|
|||
.step-item.completed .step-number { |
|||
background: #67c23a; |
|||
color: white; |
|||
} |
|||
|
|||
.step-content { |
|||
flex: 1; |
|||
} |
|||
|
|||
.step-title { |
|||
font-size: 13px; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 3px; |
|||
} |
|||
|
|||
.step-desc { |
|||
font-size: 11px; |
|||
color: #666; |
|||
} |
|||
</style> |
|||
|
|||
<style> |
|||
.panel-dialog { |
|||
position: absolute !important; |
|||
left: 170px !important; |
|||
bottom: 20px !important; |
|||
top: auto !important; |
|||
margin: 0 !important; |
|||
max-height: 400px; |
|||
} |
|||
|
|||
.panel-dialog .el-dialog__header { |
|||
padding: 12px 15px; |
|||
background: #409EFF; |
|||
color: white; |
|||
} |
|||
|
|||
.panel-dialog .el-dialog__title { |
|||
color: white; |
|||
font-size: 14px; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.panel-dialog .el-dialog__headerbtn .el-dialog__close { |
|||
color: white; |
|||
} |
|||
|
|||
.panel-dialog .el-dialog__body { |
|||
padding: 15px; |
|||
max-height: 350px; |
|||
overflow-y: auto; |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue