|
|
|
@ -2,16 +2,161 @@ |
|
|
|
<el-dialog |
|
|
|
title="编辑航线" |
|
|
|
:visible.sync="visible" |
|
|
|
width="300px" |
|
|
|
width="560px" |
|
|
|
:close-on-click-modal="false" |
|
|
|
append-to-body |
|
|
|
custom-class="blue-dialog" |
|
|
|
custom-class="blue-dialog route-edit-dialog" |
|
|
|
> |
|
|
|
<el-form :model="form" label-width="70px" size="small" @submit.native.prevent> |
|
|
|
<el-form-item label="航线名称"> |
|
|
|
<el-input v-model="form.name" placeholder="请输入航线名称"></el-input> |
|
|
|
</el-form-item> |
|
|
|
</el-form> |
|
|
|
<el-tabs v-model="activeTab" type="card" class="route-edit-tabs"> |
|
|
|
<el-tab-pane label="基础" name="basic"> |
|
|
|
<div class="tab-pane-body basic-tab-content"> |
|
|
|
<el-form :model="form" label-width="88px" size="small" class="route-edit-form" @submit.native.prevent> |
|
|
|
<el-form-item label="航线名称"> |
|
|
|
<el-input v-model="form.name" placeholder="请输入航线名称" clearable /> |
|
|
|
</el-form-item> |
|
|
|
<el-divider content-position="left">航点样式</el-divider> |
|
|
|
<div class="form-row two-col"> |
|
|
|
<el-form-item label="大小" class="col-item"> |
|
|
|
<el-slider v-model="styleForm.waypoint.pixelSize" :min="4" :max="20" :step="1" show-input /> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item label="边框粗细" class="col-item"> |
|
|
|
<el-slider v-model="styleForm.waypoint.outlineWidth" :min="1" :max="5" :step="1" show-input /> |
|
|
|
</el-form-item> |
|
|
|
</div> |
|
|
|
<div class="form-row two-col"> |
|
|
|
<el-form-item label="填充颜色" class="col-item"> |
|
|
|
<div class="color-picker-wrap"> |
|
|
|
<el-color-picker v-model="styleForm.waypoint.color" size="small" :predefine="presetColors" /> |
|
|
|
<span class="color-value">{{ styleForm.waypoint.color }}</span> |
|
|
|
</div> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item label="边框颜色" class="col-item"> |
|
|
|
<div class="color-picker-wrap"> |
|
|
|
<el-color-picker v-model="styleForm.waypoint.outlineColor" size="small" :predefine="presetColors" /> |
|
|
|
<span class="color-value">{{ styleForm.waypoint.outlineColor }}</span> |
|
|
|
</div> |
|
|
|
</el-form-item> |
|
|
|
</div> |
|
|
|
<el-divider content-position="left">航线样式</el-divider> |
|
|
|
<div class="form-row two-col"> |
|
|
|
<el-form-item label="线段样式" class="col-item"> |
|
|
|
<el-select v-model="styleForm.line.style" placeholder="请选择" style="width: 100%"> |
|
|
|
<el-option label="实线" value="solid" /> |
|
|
|
<el-option label="虚线" value="dash" /> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item label="线宽" class="col-item"> |
|
|
|
<el-slider v-model="styleForm.line.width" :min="2" :max="12" :step="1" show-input /> |
|
|
|
</el-form-item> |
|
|
|
</div> |
|
|
|
<div class="form-row two-col"> |
|
|
|
<el-form-item label="线条颜色" class="col-item"> |
|
|
|
<div class="color-picker-wrap"> |
|
|
|
<el-color-picker v-model="styleForm.line.color" size="small" :predefine="presetColors" /> |
|
|
|
<span class="color-value">{{ styleForm.line.color }}</span> |
|
|
|
</div> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item v-if="styleForm.line.style === 'dash'" label="间隔颜色" class="col-item"> |
|
|
|
<div class="color-picker-wrap"> |
|
|
|
<el-color-picker v-model="styleForm.line.gapColor" size="small" :predefine="presetColors" /> |
|
|
|
<span class="color-value">{{ styleForm.line.gapColor }}</span> |
|
|
|
</div> |
|
|
|
</el-form-item> |
|
|
|
</div> |
|
|
|
</el-form> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
<el-tab-pane label="平台" name="platform"> |
|
|
|
<div class="tab-pane-body platform-tab-content"> |
|
|
|
<div v-if="platformLoading" class="platform-loading">加载中...</div> |
|
|
|
<template v-else> |
|
|
|
<el-tabs v-model="platformCategory" type="border-card" size="small"> |
|
|
|
<el-tab-pane label="空中" name="Air"> |
|
|
|
<div class="platform-list"> |
|
|
|
<div |
|
|
|
v-for="platform in airPlatforms" |
|
|
|
:key="platform.id" |
|
|
|
class="platform-item" |
|
|
|
> |
|
|
|
<div class="platform-icon"> |
|
|
|
<img v-if="isImg(platform.imageUrl)" :src="formatImg(platform.imageUrl)" class="platform-img" /> |
|
|
|
<i v-else :class="platform.icon || 'el-icon-picture-outline'"></i> |
|
|
|
</div> |
|
|
|
<div class="platform-info"> |
|
|
|
<div class="platform-name">{{ platform.name }}</div> |
|
|
|
</div> |
|
|
|
<el-button |
|
|
|
size="mini" |
|
|
|
type="primary" |
|
|
|
:plain="selectedPlatformId !== platform.id" |
|
|
|
class="select-btn" |
|
|
|
@click="selectPlatform(platform)" |
|
|
|
> |
|
|
|
{{ selectedPlatformId === platform.id ? '已选择' : '选择' }} |
|
|
|
</el-button> |
|
|
|
</div> |
|
|
|
<div v-if="airPlatforms.length === 0" class="empty-tip">暂无空中平台</div> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
<el-tab-pane label="海上" name="Sea"> |
|
|
|
<div class="platform-list"> |
|
|
|
<div |
|
|
|
v-for="platform in seaPlatforms" |
|
|
|
:key="platform.id" |
|
|
|
class="platform-item" |
|
|
|
> |
|
|
|
<div class="platform-icon"> |
|
|
|
<img v-if="isImg(platform.imageUrl)" :src="formatImg(platform.imageUrl)" class="platform-img" /> |
|
|
|
<i v-else :class="platform.icon || 'el-icon-picture-outline'"></i> |
|
|
|
</div> |
|
|
|
<div class="platform-info"> |
|
|
|
<div class="platform-name">{{ platform.name }}</div> |
|
|
|
</div> |
|
|
|
<el-button |
|
|
|
size="mini" |
|
|
|
type="primary" |
|
|
|
:plain="selectedPlatformId !== platform.id" |
|
|
|
class="select-btn" |
|
|
|
@click="selectPlatform(platform)" |
|
|
|
> |
|
|
|
{{ selectedPlatformId === platform.id ? '已选择' : '选择' }} |
|
|
|
</el-button> |
|
|
|
</div> |
|
|
|
<div v-if="seaPlatforms.length === 0" class="empty-tip">暂无海上平台</div> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
<el-tab-pane label="地面" name="Ground"> |
|
|
|
<div class="platform-list"> |
|
|
|
<div |
|
|
|
v-for="platform in groundPlatforms" |
|
|
|
:key="platform.id" |
|
|
|
class="platform-item" |
|
|
|
> |
|
|
|
<div class="platform-icon"> |
|
|
|
<img v-if="isImg(platform.imageUrl)" :src="formatImg(platform.imageUrl)" class="platform-img" /> |
|
|
|
<i v-else :class="platform.icon || 'el-icon-picture-outline'"></i> |
|
|
|
</div> |
|
|
|
<div class="platform-info"> |
|
|
|
<div class="platform-name">{{ platform.name }}</div> |
|
|
|
</div> |
|
|
|
<el-button |
|
|
|
size="mini" |
|
|
|
type="primary" |
|
|
|
:plain="selectedPlatformId !== platform.id" |
|
|
|
class="select-btn" |
|
|
|
@click="selectPlatform(platform)" |
|
|
|
> |
|
|
|
{{ selectedPlatformId === platform.id ? '已选择' : '选择' }} |
|
|
|
</el-button> |
|
|
|
</div> |
|
|
|
<div v-if="groundPlatforms.length === 0" class="empty-tip">暂无地面平台</div> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
</el-tabs> |
|
|
|
</template> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
</el-tabs> |
|
|
|
|
|
|
|
<span slot="footer" class="dialog-footer"> |
|
|
|
<el-button size="mini" @click="visible = false">取 消</el-button> |
|
|
|
@ -21,57 +166,166 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { listLib } from '@/api/system/lib' |
|
|
|
|
|
|
|
export default { |
|
|
|
name: 'RouteEditDialog', |
|
|
|
props: { |
|
|
|
// 配合父组件的 v-model="showRouteDialog" |
|
|
|
value: { |
|
|
|
type: Boolean, |
|
|
|
default: false |
|
|
|
}, |
|
|
|
route: { |
|
|
|
type: Object, |
|
|
|
default: () => ({}) |
|
|
|
} |
|
|
|
value: { type: Boolean, default: false }, |
|
|
|
route: { type: Object, default: () => ({}) } |
|
|
|
}, |
|
|
|
data() { |
|
|
|
return { |
|
|
|
activeTab: 'basic', |
|
|
|
platformCategory: 'Air', |
|
|
|
platformLoading: false, |
|
|
|
airPlatforms: [], |
|
|
|
seaPlatforms: [], |
|
|
|
groundPlatforms: [], |
|
|
|
selectedPlatformId: null, |
|
|
|
selectedPlatform: null, |
|
|
|
form: { |
|
|
|
id: '', |
|
|
|
name: '' |
|
|
|
} |
|
|
|
}, |
|
|
|
defaultStyle: { |
|
|
|
waypoint: { pixelSize: 7, color: '#ffffff', outlineColor: '#0078FF', outlineWidth: 2 }, |
|
|
|
line: { style: 'dash', width: 4, color: '#ffffff', gapColor: '#000000', dashLength: 20 } |
|
|
|
}, |
|
|
|
styleForm: { |
|
|
|
waypoint: { pixelSize: 7, color: '#ffffff', outlineColor: '#0078FF', outlineWidth: 2 }, |
|
|
|
line: { style: 'dash', width: 4, color: '#ffffff', gapColor: '#000000', dashLength: 20 } |
|
|
|
}, |
|
|
|
presetColors: [ |
|
|
|
'#ffffff', '#000000', '#0078FF', '#409EFF', '#67C23A', '#E6A23C', '#F56C6C', |
|
|
|
'#909399', '#303133', '#00CED1', '#FF1493', '#FFD700', '#4B0082', '#00FF00', '#FF4500' |
|
|
|
] |
|
|
|
} |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
visible: { |
|
|
|
get() { |
|
|
|
return this.value |
|
|
|
}, |
|
|
|
set(val) { |
|
|
|
this.$emit('input', val) |
|
|
|
} |
|
|
|
get() { return this.value }, |
|
|
|
set(val) { this.$emit('input', val) } |
|
|
|
} |
|
|
|
}, |
|
|
|
watch: { |
|
|
|
// 每次打开弹窗或 route 变化时,深拷贝数据到 form |
|
|
|
route: { |
|
|
|
handler(val) { |
|
|
|
if (val) { |
|
|
|
// 只取需要的字段,防止污染 |
|
|
|
this.form = { |
|
|
|
id: val.id, |
|
|
|
name: val.name |
|
|
|
name: val.name, |
|
|
|
attributes: val.attributes != null ? val.attributes : '' |
|
|
|
} |
|
|
|
this.selectedPlatformId = val.platformId || null |
|
|
|
this.selectedPlatform = val.platform ? { ...val.platform } : null |
|
|
|
this.parseStyleFromRoute(val) |
|
|
|
} |
|
|
|
}, |
|
|
|
immediate: true, |
|
|
|
deep: true |
|
|
|
}, |
|
|
|
visible(val) { |
|
|
|
if (val && this.activeTab === 'platform') { |
|
|
|
this.loadPlatforms() |
|
|
|
} |
|
|
|
}, |
|
|
|
activeTab(val) { |
|
|
|
if (val === 'platform' && this.visible) { |
|
|
|
this.loadPlatforms() |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
loadPlatforms() { |
|
|
|
this.platformLoading = true |
|
|
|
listLib().then(res => { |
|
|
|
const allData = res.rows || [] |
|
|
|
this.airPlatforms = [] |
|
|
|
this.seaPlatforms = [] |
|
|
|
this.groundPlatforms = [] |
|
|
|
allData.forEach(item => { |
|
|
|
const platform = { |
|
|
|
id: item.id, |
|
|
|
name: item.name, |
|
|
|
type: item.type, |
|
|
|
imageUrl: item.iconUrl || '', |
|
|
|
icon: item.iconUrl ? '' : 'el-icon-picture-outline' |
|
|
|
} |
|
|
|
if (item.type === 'Air') this.airPlatforms.push(platform) |
|
|
|
else if (item.type === 'Sea') this.seaPlatforms.push(platform) |
|
|
|
else if (item.type === 'Ground') this.groundPlatforms.push(platform) |
|
|
|
}) |
|
|
|
}).catch(() => { |
|
|
|
this.$message.error('加载平台列表失败') |
|
|
|
}).finally(() => { |
|
|
|
this.platformLoading = false |
|
|
|
}) |
|
|
|
}, |
|
|
|
isImg(path) { |
|
|
|
if (!path) return false |
|
|
|
return path.includes('/') || path.includes('data:image') |
|
|
|
}, |
|
|
|
formatImg(url) { |
|
|
|
if (!url) return '' |
|
|
|
const cleanPath = url.replace(/\/+/g, '/') |
|
|
|
const backendUrl = process.env.VUE_APP_BACKEND_URL || '' |
|
|
|
return backendUrl + cleanPath |
|
|
|
}, |
|
|
|
selectPlatform(platform) { |
|
|
|
this.selectedPlatformId = platform.id |
|
|
|
this.selectedPlatform = { id: platform.id, name: platform.name, imageUrl: platform.imageUrl } |
|
|
|
}, |
|
|
|
parseStyleFromRoute(route) { |
|
|
|
const def = this.defaultStyle |
|
|
|
try { |
|
|
|
const attrs = typeof route.attributes === 'string' ? JSON.parse(route.attributes || '{}') : (route.attributes || {}) |
|
|
|
const wp = attrs.waypointStyle || {} |
|
|
|
const ln = attrs.lineStyle || {} |
|
|
|
this.styleForm = { |
|
|
|
waypoint: { |
|
|
|
pixelSize: wp.pixelSize != null ? wp.pixelSize : def.waypoint.pixelSize, |
|
|
|
color: wp.color || def.waypoint.color, |
|
|
|
outlineColor: wp.outlineColor || def.waypoint.outlineColor, |
|
|
|
outlineWidth: wp.outlineWidth != null ? wp.outlineWidth : def.waypoint.outlineWidth |
|
|
|
}, |
|
|
|
line: { |
|
|
|
style: ln.style || def.line.style, |
|
|
|
width: ln.width != null ? ln.width : def.line.width, |
|
|
|
color: ln.color || def.line.color, |
|
|
|
gapColor: ln.gapColor != null ? ln.gapColor : def.line.gapColor, |
|
|
|
dashLength: ln.dashLength != null ? ln.dashLength : def.line.dashLength |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (_) { |
|
|
|
this.styleForm = { |
|
|
|
waypoint: { ...def.waypoint }, |
|
|
|
line: { ...def.line } |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
getAttributesForSave() { |
|
|
|
const attrs = {} |
|
|
|
try { |
|
|
|
const existing = typeof this.form.attributes === 'string' ? JSON.parse(this.form.attributes || '{}') : (this.form.attributes || {}) |
|
|
|
Object.assign(attrs, existing) |
|
|
|
} catch (_) {} |
|
|
|
attrs.waypointStyle = { ...this.styleForm.waypoint } |
|
|
|
attrs.lineStyle = { ...this.styleForm.line } |
|
|
|
return JSON.stringify(attrs) |
|
|
|
}, |
|
|
|
handleSave() { |
|
|
|
// 这里的 form 包含了修改后的 name |
|
|
|
this.$emit('save', this.form) |
|
|
|
const payload = { |
|
|
|
...this.form, |
|
|
|
attributes: this.getAttributesForSave(), |
|
|
|
platformId: this.selectedPlatformId, |
|
|
|
platform: this.selectedPlatform, |
|
|
|
routeStyle: { |
|
|
|
waypoint: { ...this.styleForm.waypoint }, |
|
|
|
line: { ...this.styleForm.line } |
|
|
|
} |
|
|
|
} |
|
|
|
this.$emit('save', payload) |
|
|
|
this.visible = false |
|
|
|
} |
|
|
|
} |
|
|
|
@ -79,6 +333,96 @@ export default { |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
.tab-pane-body { |
|
|
|
min-height: 380px; |
|
|
|
box-sizing: border-box; |
|
|
|
} |
|
|
|
.basic-tab-content { |
|
|
|
padding-right: 4px; |
|
|
|
} |
|
|
|
.basic-tab-content .form-row { |
|
|
|
display: flex; |
|
|
|
gap: 16px; |
|
|
|
margin-bottom: 0; |
|
|
|
} |
|
|
|
.basic-tab-content .form-row.two-col .col-item { |
|
|
|
flex: 1; |
|
|
|
min-width: 0; |
|
|
|
} |
|
|
|
.basic-tab-content .form-row .el-form-item { |
|
|
|
margin-bottom: 14px; |
|
|
|
} |
|
|
|
.basic-tab-content .form-row.two-col .el-form-item { |
|
|
|
margin-bottom: 14px; |
|
|
|
} |
|
|
|
.color-picker-wrap { |
|
|
|
display: inline-flex; |
|
|
|
align-items: center; |
|
|
|
gap: 8px; |
|
|
|
} |
|
|
|
.platform-tab-content { |
|
|
|
min-height: 380px; |
|
|
|
max-height: 380px; |
|
|
|
overflow-y: auto; |
|
|
|
} |
|
|
|
.platform-loading, |
|
|
|
.empty-tip { |
|
|
|
padding: 20px; |
|
|
|
color: #909399; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
.platform-list { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
gap: 8px; |
|
|
|
} |
|
|
|
.platform-item { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
padding: 8px; |
|
|
|
border: 1px solid #ebeef5; |
|
|
|
border-radius: 4px; |
|
|
|
} |
|
|
|
.platform-icon { |
|
|
|
width: 36px; |
|
|
|
height: 36px; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
margin-right: 10px; |
|
|
|
background: #f5f7fa; |
|
|
|
border-radius: 4px; |
|
|
|
} |
|
|
|
.platform-img { |
|
|
|
max-width: 32px; |
|
|
|
max-height: 32px; |
|
|
|
object-fit: contain; |
|
|
|
} |
|
|
|
.platform-info { |
|
|
|
flex: 1; |
|
|
|
min-width: 0; |
|
|
|
} |
|
|
|
.platform-name { |
|
|
|
font-size: 13px; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
.select-btn { |
|
|
|
flex-shrink: 0; |
|
|
|
} |
|
|
|
.route-edit-form .el-divider__text { |
|
|
|
font-size: 12px; |
|
|
|
color: #606266; |
|
|
|
} |
|
|
|
.route-edit-form .color-value { |
|
|
|
font-size: 12px; |
|
|
|
color: #909399; |
|
|
|
vertical-align: middle; |
|
|
|
} |
|
|
|
.route-edit-form .el-slider { |
|
|
|
margin-right: 12px; |
|
|
|
} |
|
|
|
.blue-btn { |
|
|
|
background: #008aff; |
|
|
|
border-color: #008aff; |
|
|
|
|