|
|
|
|
<template>
|
|
|
|
|
<div class="context-menu" v-if="visible" :style="positionStyle">
|
|
|
|
|
<div class="menu-section" v-if="!entityData || (entityData.type !== 'routePlatform' && entityData.type !== 'route')">
|
|
|
|
|
<div class="menu-item" @click="handleDelete">
|
|
|
|
|
<span class="menu-icon">🗑️</span>
|
|
|
|
|
<span>删除</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 航线:上锁/解锁、复制 -->
|
|
|
|
|
<div class="menu-section" v-if="entityData && entityData.type === 'route'">
|
|
|
|
|
<div class="menu-title">航线编辑</div>
|
|
|
|
|
<div class="menu-item" @click="handleToggleRouteLock">
|
|
|
|
|
<span class="menu-icon">{{ isRouteLocked ? '🔓' : '🔒' }}</span>
|
|
|
|
|
<span>{{ isRouteLocked ? '解锁' : '上锁' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="handleCopyRoute">
|
|
|
|
|
<span class="menu-icon">📋</span>
|
|
|
|
|
<span>复制</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 航线上飞机:显示/隐藏/编辑标牌、编辑平台 -->
|
|
|
|
|
<div class="menu-section" v-if="entityData && entityData.type === 'routePlatform'">
|
|
|
|
|
<div class="menu-title">飞机标牌</div>
|
|
|
|
|
<div class="menu-item" @click="handleToggleRouteLabel">
|
|
|
|
|
<span class="menu-icon">🏷️</span>
|
|
|
|
|
<span>{{ entityData.labelVisible ? '隐藏标牌' : '显示标牌' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="handleEditPlatform">
|
|
|
|
|
<span class="menu-icon">📝</span>
|
|
|
|
|
<span>编辑</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="handlePowerZone">
|
|
|
|
|
<span class="menu-icon">⭕</span>
|
|
|
|
|
<span>威力区</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 线段特有选项 -->
|
|
|
|
|
<div class="menu-section" v-if="entityData.type === 'line' && !entityData.routeId">
|
|
|
|
|
<div class="menu-title">线段属性</div>
|
|
|
|
|
<div class="menu-item" @click="toggleColorPicker('color')">
|
|
|
|
|
<span class="menu-icon">🎨</span>
|
|
|
|
|
<span>颜色</span>
|
|
|
|
|
<span class="menu-preview" :style="{backgroundColor: entityData.color}"></span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 颜色选择器 -->
|
|
|
|
|
<div class="color-picker-container" v-if="showColorPickerFor === 'color'">
|
|
|
|
|
<div class="color-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="color in presetColors"
|
|
|
|
|
:key="color"
|
|
|
|
|
class="color-item"
|
|
|
|
|
:style="{backgroundColor: color}"
|
|
|
|
|
@click="selectColor('color', color)"
|
|
|
|
|
:class="{ active: entityData.color === color }"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleWidthPicker">
|
|
|
|
|
<span class="menu-icon">📏</span>
|
|
|
|
|
<span>线宽</span>
|
|
|
|
|
<span class="menu-value">{{ entityData.width }}px</span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 线宽选择器 -->
|
|
|
|
|
<div class="width-picker-container" v-if="showWidthPicker">
|
|
|
|
|
<div class="width-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="width in presetWidths"
|
|
|
|
|
:key="width"
|
|
|
|
|
class="width-item"
|
|
|
|
|
@click="selectWidth(width)"
|
|
|
|
|
:class="{ active: entityData.width === width }"
|
|
|
|
|
>
|
|
|
|
|
{{ width }}px
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleBearingTypeMenu">
|
|
|
|
|
<span class="menu-icon">🧭</span>
|
|
|
|
|
<span>方位角类型</span>
|
|
|
|
|
<span class="menu-value">{{ entityData.bearingType === 'magnetic' ? '磁方位' : '真方位' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 方位角类型选择菜单 -->
|
|
|
|
|
<div class="sub-menu" v-if="showBearingTypeMenu">
|
|
|
|
|
<div class="sub-menu-item" @click="selectBearingType('true')" :class="{ active: entityData.bearingType === 'true' }">
|
|
|
|
|
<span>真方位</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="sub-menu-item" @click="selectBearingType('magnetic')" :class="{ active: entityData.bearingType === 'magnetic' }">
|
|
|
|
|
<span>磁方位</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 点特有选项 -->
|
|
|
|
|
<div class="menu-section" v-if="entityData.type === 'point'">
|
|
|
|
|
<div class="menu-title">点属性</div>
|
|
|
|
|
<div class="menu-item" @click="toggleColorPicker('color')">
|
|
|
|
|
<span class="menu-icon">🎨</span>
|
|
|
|
|
<span>颜色</span>
|
|
|
|
|
<span class="menu-preview" :style="{backgroundColor: entityData.color}"></span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 颜色选择器 -->
|
|
|
|
|
<div class="color-picker-container" v-if="showColorPickerFor === 'color'">
|
|
|
|
|
<div class="color-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="color in presetColors"
|
|
|
|
|
:key="color"
|
|
|
|
|
class="color-item"
|
|
|
|
|
:style="{backgroundColor: color}"
|
|
|
|
|
@click="selectColor('color', color)"
|
|
|
|
|
:class="{ active: entityData.color === color }"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleSizePicker">
|
|
|
|
|
<span class="menu-icon">🔵</span>
|
|
|
|
|
<span>大小</span>
|
|
|
|
|
<span class="menu-value">{{ entityData.size }}px</span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 大小选择器 -->
|
|
|
|
|
<div class="size-picker-container" v-if="showSizePicker">
|
|
|
|
|
<div class="size-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="size in presetSizes"
|
|
|
|
|
:key="size"
|
|
|
|
|
class="size-item"
|
|
|
|
|
@click="selectSize(size)"
|
|
|
|
|
:class="{ active: entityData.size === size }"
|
|
|
|
|
>
|
|
|
|
|
{{ size }}px
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 多边形特有选项 -->
|
|
|
|
|
<div class="menu-section" v-if="entityData.type === 'polygon' || entityData.type === 'rectangle' || entityData.type === 'circle' || entityData.type === 'sector' || entityData.type === 'powerZone'">
|
|
|
|
|
<div class="menu-title">填充属性</div>
|
|
|
|
|
<div class="menu-item" @click="editName" v-if="entityData.type === 'powerZone'">
|
|
|
|
|
<span class="menu-icon">📝</span>
|
|
|
|
|
<span>名称</span>
|
|
|
|
|
<span class="menu-value">{{ entityData.name || '' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleColorPicker('color')">
|
|
|
|
|
<span class="menu-icon">🎨</span>
|
|
|
|
|
<span>填充色</span>
|
|
|
|
|
<span class="menu-preview" :style="{backgroundColor: entityData.color}"></span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 颜色选择器 -->
|
|
|
|
|
<div class="color-picker-container" v-if="showColorPickerFor === 'color'">
|
|
|
|
|
<div class="color-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="color in presetColors"
|
|
|
|
|
:key="color"
|
|
|
|
|
class="color-item"
|
|
|
|
|
:style="{backgroundColor: color}"
|
|
|
|
|
@click="selectColor('color', color)"
|
|
|
|
|
:class="{ active: entityData.color === color }"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleOpacityPicker">
|
|
|
|
|
<span class="menu-icon">🌫️</span>
|
|
|
|
|
<span>透明度</span>
|
|
|
|
|
<span class="menu-value">{{ Math.round(entityData.opacity * 100) }}%</span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 透明度选择器 -->
|
|
|
|
|
<div class="opacity-picker-container" v-if="showOpacityPicker">
|
|
|
|
|
<div class="opacity-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="opacity in presetOpacities"
|
|
|
|
|
:key="opacity"
|
|
|
|
|
class="opacity-item"
|
|
|
|
|
@click="selectOpacity(opacity)"
|
|
|
|
|
:class="{ active: Math.round(entityData.opacity * 100) === Math.round(opacity * 100) }"
|
|
|
|
|
>
|
|
|
|
|
{{ Math.round(opacity * 100) }}%
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleColorPicker('borderColor')">
|
|
|
|
|
<span class="menu-icon">🖌️</span>
|
|
|
|
|
<span>边框色</span>
|
|
|
|
|
<span class="menu-preview" :style="{backgroundColor: entityData.borderColor || entityData.color}"></span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 边框颜色选择器 -->
|
|
|
|
|
<div class="color-picker-container" v-if="showColorPickerFor === 'borderColor'">
|
|
|
|
|
<div class="color-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="color in presetColors"
|
|
|
|
|
:key="color"
|
|
|
|
|
class="color-item"
|
|
|
|
|
:style="{backgroundColor: color}"
|
|
|
|
|
@click="selectColor('borderColor', color)"
|
|
|
|
|
:class="{ active: (entityData.borderColor || entityData.color) === color }"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleWidthPicker">
|
|
|
|
|
<span class="menu-icon">📏</span>
|
|
|
|
|
<span>边框宽</span>
|
|
|
|
|
<span class="menu-value">{{ entityData.width }}px</span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 边框宽度选择器 -->
|
|
|
|
|
<div class="width-picker-container" v-if="showWidthPicker">
|
|
|
|
|
<div class="width-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="width in presetWidths"
|
|
|
|
|
:key="width"
|
|
|
|
|
class="width-item"
|
|
|
|
|
@click="selectWidth(width)"
|
|
|
|
|
:class="{ active: entityData.width === width }"
|
|
|
|
|
>
|
|
|
|
|
{{ width }}px
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 箭头特有选项 -->
|
|
|
|
|
<div class="menu-section" v-if="entityData.type === 'arrow'">
|
|
|
|
|
<div class="menu-title">箭头属性</div>
|
|
|
|
|
<div class="menu-item" @click="toggleColorPicker('color')">
|
|
|
|
|
<span class="menu-icon">🎨</span>
|
|
|
|
|
<span>颜色</span>
|
|
|
|
|
<span class="menu-preview" :style="{backgroundColor: entityData.color}"></span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 颜色选择器 -->
|
|
|
|
|
<div class="color-picker-container" v-if="showColorPickerFor === 'color'">
|
|
|
|
|
<div class="color-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="color in presetColors"
|
|
|
|
|
:key="color"
|
|
|
|
|
class="color-item"
|
|
|
|
|
:style="{backgroundColor: color}"
|
|
|
|
|
@click="selectColor('color', color)"
|
|
|
|
|
:class="{ active: entityData.color === color }"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleWidthPicker">
|
|
|
|
|
<span class="menu-icon">📏</span>
|
|
|
|
|
<span>线宽</span>
|
|
|
|
|
<span class="menu-value">{{ entityData.width }}px</span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 线宽选择器 -->
|
|
|
|
|
<div class="width-picker-container" v-if="showWidthPicker">
|
|
|
|
|
<div class="width-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="width in presetWidths"
|
|
|
|
|
:key="width"
|
|
|
|
|
class="width-item"
|
|
|
|
|
@click="selectWidth(width)"
|
|
|
|
|
:class="{ active: entityData.width === width }"
|
|
|
|
|
>
|
|
|
|
|
{{ width }}px
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 文本特有选项 -->
|
|
|
|
|
<div class="menu-section" v-if="entityData.type === 'text'">
|
|
|
|
|
<div class="menu-title">文本属性</div>
|
|
|
|
|
<div class="menu-item" @click="toggleColorPicker('color')">
|
|
|
|
|
<span class="menu-icon">🎨</span>
|
|
|
|
|
<span>文字颜色</span>
|
|
|
|
|
<span class="menu-preview" :style="{backgroundColor: entityData.color}"></span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 颜色选择器 -->
|
|
|
|
|
<div class="color-picker-container" v-if="showColorPickerFor === 'color'">
|
|
|
|
|
<div class="color-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="color in presetColors"
|
|
|
|
|
:key="color"
|
|
|
|
|
class="color-item"
|
|
|
|
|
:style="{backgroundColor: color}"
|
|
|
|
|
@click="selectColor('color', color)"
|
|
|
|
|
:class="{ active: entityData.color === color }"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="showFontPicker">
|
|
|
|
|
<span class="menu-icon">📝</span>
|
|
|
|
|
<span>字体</span>
|
|
|
|
|
<span class="menu-value">{{ getFontName(entityData.font) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="toggleFontSizePicker">
|
|
|
|
|
<span class="menu-icon">🔤</span>
|
|
|
|
|
<span>字号</span>
|
|
|
|
|
<span class="menu-value">{{ getFontSize(entityData.font) }}px</span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 字号选择器 -->
|
|
|
|
|
<div class="font-size-picker-container" v-if="showFontSizePicker">
|
|
|
|
|
<div class="font-size-grid">
|
|
|
|
|
<div
|
|
|
|
|
v-for="size in presetFontSizes"
|
|
|
|
|
:key="size"
|
|
|
|
|
class="font-size-item"
|
|
|
|
|
@click="selectFontSize(size)"
|
|
|
|
|
:class="{ active: getFontSize(entityData.font) === size.toString() }"
|
|
|
|
|
>
|
|
|
|
|
{{ size }}px
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 平台图标(拖拽到地图的图标)特有选项 -->
|
|
|
|
|
<div class="menu-section" v-if="entityData.type === 'platformIcon'">
|
|
|
|
|
<div class="menu-title">平台图标</div>
|
|
|
|
|
<div class="menu-item" @click.stop="handleShowTransformBox">
|
|
|
|
|
<span class="menu-icon">📐</span>
|
|
|
|
|
<span>显示伸缩框</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="handleEditPlatformPosition">
|
|
|
|
|
<span class="menu-icon">📍</span>
|
|
|
|
|
<span>修改位置</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="handleEditPlatformHeading">
|
|
|
|
|
<span class="menu-icon">🧭</span>
|
|
|
|
|
<span>修改朝向</span>
|
|
|
|
|
<span class="menu-value">{{ entityData.heading != null ? entityData.heading + '°' : '0°' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-title" style="margin-top:8px;">航线</div>
|
|
|
|
|
<div class="menu-item" @click="handleStartRouteBeforePlatform">
|
|
|
|
|
<span class="menu-icon">⬅️</span>
|
|
|
|
|
<span>在此之前插入航线</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="menu-item" @click="handleStartRouteAfterPlatform">
|
|
|
|
|
<span class="menu-icon">➡️</span>
|
|
|
|
|
<span>在此之后插入航线</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
export default {
|
|
|
|
|
name: 'ContextMenu',
|
|
|
|
|
props: {
|
|
|
|
|
visible: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false
|
|
|
|
|
},
|
|
|
|
|
position: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default: () => ({ x: 0, y: 0 })
|
|
|
|
|
},
|
|
|
|
|
entityData: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default: null
|
|
|
|
|
},
|
|
|
|
|
routeLocked: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default: () => ({})
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
showColorPickerFor: null,
|
|
|
|
|
showWidthPicker: false,
|
|
|
|
|
showSizePicker: false,
|
|
|
|
|
showOpacityPicker: false,
|
|
|
|
|
showFontSizePicker: false,
|
|
|
|
|
showBearingTypeMenu: false,
|
|
|
|
|
presetColors: [
|
|
|
|
|
'#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF',
|
|
|
|
|
'#FF6600', '#663399', '#999999', '#000000', '#FFFFFF', '#FF99CC',
|
|
|
|
|
'#CC99FF', '#99CCFF', '#99FF99', '#FFFF99', '#FFCC99', '#FF9999'
|
|
|
|
|
],
|
|
|
|
|
presetWidths: [1, 2, 3, 4, 5, 6, 8, 10, 12],
|
|
|
|
|
presetSizes: [6, 8, 10, 12, 14, 16, 18, 20, 24],
|
|
|
|
|
presetOpacities: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
|
|
|
|
|
presetFontSizes: [8, 10, 12, 14, 16, 18, 20, 24, 28, 32]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
positionStyle() {
|
|
|
|
|
return {
|
|
|
|
|
left: this.position.x + 'px',
|
|
|
|
|
top: this.position.y + 'px'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
isRouteLocked() {
|
|
|
|
|
if (!this.entityData || this.entityData.type !== 'route' || this.entityData.routeId == null) return false
|
|
|
|
|
return !!this.routeLocked[this.entityData.routeId]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
handleDelete() {
|
|
|
|
|
this.$emit('delete')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleShowTransformBox() {
|
|
|
|
|
this.$emit('show-transform-box')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleEditPlatformPosition() {
|
|
|
|
|
this.$emit('edit-platform-position')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleEditPlatformHeading() {
|
|
|
|
|
this.$emit('edit-platform-heading')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleStartRouteBeforePlatform() {
|
|
|
|
|
this.$emit('start-route-before-platform', this.entityData)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleStartRouteAfterPlatform() {
|
|
|
|
|
this.$emit('start-route-after-platform', this.entityData)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleToggleRouteLabel() {
|
|
|
|
|
this.$emit('toggle-route-label')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
editName() {
|
|
|
|
|
const newName = prompt('请输入威力区名称:', this.entityData.name || '')
|
|
|
|
|
if (newName !== null && newName.trim() !== '') {
|
|
|
|
|
this.$emit('update-property', 'name', newName.trim())
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleToggleRouteLock() {
|
|
|
|
|
this.$emit('toggle-route-lock')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleCopyRoute() {
|
|
|
|
|
this.$emit('copy-route')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleEditPlatform() {
|
|
|
|
|
this.$emit('edit-platform')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handlePowerZone() {
|
|
|
|
|
this.$emit('power-zone')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
toggleColorPicker(property) {
|
|
|
|
|
if (this.showColorPickerFor === property) {
|
|
|
|
|
this.showColorPickerFor = null
|
|
|
|
|
} else {
|
|
|
|
|
// 隐藏其他选择器
|
|
|
|
|
this.showWidthPicker = false
|
|
|
|
|
this.showSizePicker = false
|
|
|
|
|
this.showOpacityPicker = false
|
|
|
|
|
this.showFontSizePicker = false
|
|
|
|
|
this.showColorPickerFor = property
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
selectColor(property, color) {
|
|
|
|
|
this.$emit('update-property', property, color)
|
|
|
|
|
this.showColorPickerFor = null
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
toggleWidthPicker() {
|
|
|
|
|
if (this.showWidthPicker) {
|
|
|
|
|
this.showWidthPicker = false
|
|
|
|
|
} else {
|
|
|
|
|
// 隐藏其他选择器
|
|
|
|
|
this.showColorPickerFor = null
|
|
|
|
|
this.showSizePicker = false
|
|
|
|
|
this.showOpacityPicker = false
|
|
|
|
|
this.showFontSizePicker = false
|
|
|
|
|
this.showWidthPicker = true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
selectWidth(width) {
|
|
|
|
|
this.$emit('update-property', 'width', width)
|
|
|
|
|
this.showWidthPicker = false
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
toggleSizePicker() {
|
|
|
|
|
if (this.showSizePicker) {
|
|
|
|
|
this.showSizePicker = false
|
|
|
|
|
} else {
|
|
|
|
|
// 隐藏其他选择器
|
|
|
|
|
this.showColorPickerFor = null
|
|
|
|
|
this.showWidthPicker = false
|
|
|
|
|
this.showOpacityPicker = false
|
|
|
|
|
this.showFontSizePicker = false
|
|
|
|
|
this.showSizePicker = true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
selectSize(size) {
|
|
|
|
|
this.$emit('update-property', 'size', size)
|
|
|
|
|
this.showSizePicker = false
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
toggleOpacityPicker() {
|
|
|
|
|
if (this.showOpacityPicker) {
|
|
|
|
|
this.showOpacityPicker = false
|
|
|
|
|
} else {
|
|
|
|
|
// 隐藏其他选择器
|
|
|
|
|
this.showColorPickerFor = null
|
|
|
|
|
this.showWidthPicker = false
|
|
|
|
|
this.showSizePicker = false
|
|
|
|
|
this.showFontSizePicker = false
|
|
|
|
|
this.showOpacityPicker = true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
selectOpacity(opacity) {
|
|
|
|
|
this.$emit('update-property', 'opacity', opacity)
|
|
|
|
|
this.showOpacityPicker = false
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
toggleFontSizePicker() {
|
|
|
|
|
if (this.showFontSizePicker) {
|
|
|
|
|
this.showFontSizePicker = false
|
|
|
|
|
} else {
|
|
|
|
|
// 隐藏其他选择器
|
|
|
|
|
this.showColorPickerFor = null
|
|
|
|
|
this.showWidthPicker = false
|
|
|
|
|
this.showSizePicker = false
|
|
|
|
|
this.showOpacityPicker = false
|
|
|
|
|
this.showFontSizePicker = true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
selectFontSize(size) {
|
|
|
|
|
const fontName = this.getFontName(this.entityData.font)
|
|
|
|
|
this.$emit('update-property', 'font', `${size}px ${fontName}`)
|
|
|
|
|
this.showFontSizePicker = false
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
showFontPicker() {
|
|
|
|
|
const fonts = ['Arial', 'Microsoft YaHei', 'SimSun', 'SimHei', 'KaiTi']
|
|
|
|
|
const fontName = prompt(`请选择字体:\n${fonts.map((f, i) => `${i+1}. ${f}`).join('\n')}\n\n输入序号:`)
|
|
|
|
|
if (fontName && !isNaN(fontName) && fonts[parseInt(fontName)-1]) {
|
|
|
|
|
const selectedFont = fonts[parseInt(fontName)-1]
|
|
|
|
|
const fontSize = this.getFontSize(this.entityData.font)
|
|
|
|
|
this.$emit('update-property', 'font', `${fontSize}px ${selectedFont}`)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getFontName(font) {
|
|
|
|
|
const match = font.match(/\d+px\s+(.+)/)
|
|
|
|
|
return match ? match[1] : 'Arial'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getFontSize(font) {
|
|
|
|
|
const match = font.match(/(\d+)px/)
|
|
|
|
|
return match ? match[1] : '14'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
toggleBearingTypeMenu() {
|
|
|
|
|
// 切换方位角类型选择菜单的显示/隐藏
|
|
|
|
|
this.showBearingTypeMenu = !this.showBearingTypeMenu
|
|
|
|
|
// 隐藏其他选择器
|
|
|
|
|
if (this.showBearingTypeMenu) {
|
|
|
|
|
this.showColorPickerFor = null
|
|
|
|
|
this.showWidthPicker = false
|
|
|
|
|
this.showSizePicker = false
|
|
|
|
|
this.showOpacityPicker = false
|
|
|
|
|
this.showFontSizePicker = false
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
selectBearingType(bearingType) {
|
|
|
|
|
// 选择方位角类型
|
|
|
|
|
this.$emit('update-property', 'bearingType', bearingType)
|
|
|
|
|
this.showBearingTypeMenu = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.context-menu {
|
|
|
|
|
position: fixed;
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
background: white;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
|
|
|
|
padding: 8px 0;
|
|
|
|
|
min-width: 180px;
|
|
|
|
|
max-width: 220px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-section {
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-section:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-title {
|
|
|
|
|
padding: 4px 16px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #666;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-item:hover {
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-icon {
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-preview {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-value {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #666;
|
|
|
|
|
min-width: 40px;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.menu-item span {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 颜色选择器样式 */
|
|
|
|
|
.color-picker-container {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
background-color: #f9f9f9;
|
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.color-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(6, 1fr);
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.color-item {
|
|
|
|
|
width: 24px;
|
|
|
|
|
height: 24px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.color-item:hover {
|
|
|
|
|
transform: scale(1.1);
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.color-item.active {
|
|
|
|
|
transform: scale(1.2);
|
|
|
|
|
box-shadow: 0 0 0 2px white, 0 0 0 3px #007bff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 线宽选择器样式 */
|
|
|
|
|
.width-picker-container {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
background-color: #f9f9f9;
|
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.width-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.width-item {
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
background-color: white;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.width-item:hover {
|
|
|
|
|
background-color: #e3f2fd;
|
|
|
|
|
border-color: #2196f3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.width-item.active {
|
|
|
|
|
background-color: #2196f3;
|
|
|
|
|
color: white;
|
|
|
|
|
border-color: #1976d2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 大小选择器样式 */
|
|
|
|
|
.size-picker-container {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
background-color: #f9f9f9;
|
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.size-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.size-item {
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
background-color: white;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.size-item:hover {
|
|
|
|
|
background-color: #e3f2fd;
|
|
|
|
|
border-color: #2196f3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.size-item.active {
|
|
|
|
|
background-color: #2196f3;
|
|
|
|
|
color: white;
|
|
|
|
|
border-color: #1976d2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 透明度选择器样式 */
|
|
|
|
|
.opacity-picker-container {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
background-color: #f9f9f9;
|
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.opacity-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(5, 1fr);
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.opacity-item {
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
background-color: white;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.opacity-item:hover {
|
|
|
|
|
background-color: #e3f2fd;
|
|
|
|
|
border-color: #2196f3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.opacity-item.active {
|
|
|
|
|
background-color: #2196f3;
|
|
|
|
|
color: white;
|
|
|
|
|
border-color: #1976d2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 字号选择器样式 */
|
|
|
|
|
.font-size-picker-container {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
background-color: #f9f9f9;
|
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.font-size-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(5, 1fr);
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.font-size-item {
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
background-color: white;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.font-size-item:hover {
|
|
|
|
|
background-color: #e3f2fd;
|
|
|
|
|
border-color: #2196f3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.font-size-item.active {
|
|
|
|
|
background-color: #2196f3;
|
|
|
|
|
color: white;
|
|
|
|
|
border-color: #1976d2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 子菜单样式 */
|
|
|
|
|
.sub-menu {
|
|
|
|
|
background-color: #f9f9f9;
|
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sub-menu-item {
|
|
|
|
|
padding: 6px 16px 6px 32px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sub-menu-item:hover {
|
|
|
|
|
background-color: #e3f2fd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sub-menu-item.active {
|
|
|
|
|
background-color: #2196f3;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
</style>
|