Files
cma-management/frontend/src/components/strategy-map/MapEditBar.vue
T

171 lines
5.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="map-editbar">
<div class="eb-left">
<el-input
:model-value="search"
class="eb-search"
size="small"
clearable
placeholder="搜索目标 / 负责人 / KPI"
@update:model-value="(v: string) => $emit('update:search', v)"
>
<template #prefix>
<svg class="eb-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
<circle cx="11" cy="11" r="7" /><path d="M20 20l-3.5-3.5" />
</svg>
</template>
</el-input>
<div class="eb-chips" role="group" aria-label="状态筛选">
<button
v-for="opt in filterOptions"
:key="opt.value"
type="button"
class="eb-chip"
:class="{ on: filter === opt.value }"
@click="$emit('update:filter', opt.value)"
>{{ opt.label }}</button>
</div>
<div class="eb-chips eb-layers" role="group" aria-label="层筛选">
<button
v-for="cfg in layerConfigs"
:key="cfg.key"
type="button"
class="eb-chip layer-chip"
:class="{ off: !visibleLayers[cfg.key] }"
:title="visibleLayers[cfg.key] ? '隐藏该层' : '显示该层'"
@click="$emit('toggle-layer', cfg.key)"
>
<span class="layer-dot" :style="{ background: cfg.color }"></span>
{{ cfg.short }}
</button>
</div>
</div>
<div class="eb-right">
<el-dropdown trigger="click" @command="onAdd">
<el-button size="small" type="primary" plain>
<svg class="eb-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M12 5v14M5 12h14" />
</svg>
加目标
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="cfg in layerConfigs" :key="cfg.key" :command="cfg.key">
<span class="dd-dot" :style="{ background: cfg.color }"></span>{{ cfg.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button size="small" @click="$emit('connect')">
<svg class="eb-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
<path d="M10 13a5 5 0 0 0 7 0l2-2a5 5 0 0 0-7-7l-1 1" />
<path d="M14 11a5 5 0 0 0-7 0l-2 2a5 5 0 0 0 7 7l1-1" />
</svg>
连因果
</el-button>
<el-button size="small" :disabled="!canUndo" @click="$emit('undo')" title="撤销上一步画布编辑">
<svg class="eb-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
<path d="M9 14L4 9l5-5" /><path d="M4 9h10a6 6 0 0 1 0 12h-3" />
</svg>
撤销
</el-button>
<el-button size="small" @click="$emit('completeness')" title="数据完备度与清单(口径:地图65·23KPI判据)">
<svg class="eb-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
<path d="M4 19V5" /><path d="M4 19h16" /><rect x="7" y="11" width="3" height="6" rx=".5" />
<rect x="12" y="7" width="3" height="10" rx=".5" /><rect x="17" y="13" width="3" height="4" rx=".5" />
</svg>
数据完备度
</el-button>
<el-button size="small" @click="$emit('fit')" title="缩放到适合窗口">
<svg class="eb-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
<path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5" />
</svg>
回全景
</el-button>
<el-button size="small" text @click="$emit('toggle-tree')">
{{ treeOpen ? '收起目标树' : '展开目标树' }}
</el-button>
</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
search: string
filter: 'all' | 'risk' | 'nodata'
visibleLayers: Record<string, boolean>
layerConfigs: { key: string; label: string; short: string; color: string }[]
canUndo: boolean
treeOpen: boolean
}>()
const emit = defineEmits<{
(e: 'update:search', v: string): void
(e: 'update:filter', v: 'all' | 'risk' | 'nodata'): void
(e: 'toggle-layer', key: string): void
(e: 'add-objective', key: string): void
(e: 'connect'): void
(e: 'undo'): void
(e: 'fit'): void
(e: 'toggle-tree'): void
(e: 'completeness'): void
}>()
const filterOptions: { value: 'all' | 'risk' | 'nodata'; label: string }[] = [
{ value: 'all', label: '全部' },
{ value: 'risk', label: '只看风险' },
{ value: 'nodata', label: '只看无数据' },
]
function onAdd(key: string) {
emit('add-objective', key)
}
</script>
<style scoped>
.map-editbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
flex-wrap: wrap;
flex-shrink: 0;
padding: 8px 10px;
margin-bottom: 10px;
background: var(--bh-bg-card, #fff);
border: 1px solid var(--bh-border, #E5E7EB);
border-radius: 8px;
}
.eb-left, .eb-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.eb-search { width: 220px; }
.eb-ico { width: 14px; height: 14px; vertical-align: -2px; }
.eb-chips { display: inline-flex; align-items: center; gap: 4px; padding: 2px; background: #F6F7F9; border: 1px solid #E5E7EB; border-radius: 6px; }
.eb-chip {
border: 0;
background: transparent;
color: #475569;
font-size: 12px;
line-height: 20px;
padding: 2px 10px;
border-radius: 4px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
transition: background .15s, color .15s;
}
.eb-chip:hover { color: #635BFF; }
.eb-chip.on { background: #635BFF; color: #fff; }
.eb-chip.off { color: #94A3B8; text-decoration: line-through; }
.layer-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }
.dd-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
</style>