wip(strategy-map): V1+V3 合版 + v2/v3 口径(完备度/C1 hold/57 归档组)

This commit is contained in:
Hermes CI Fix
2026-10-19 10:33:40 +08:00
parent 0fc2c6109e
commit 4d20c27de4
16 changed files with 2116 additions and 89 deletions
@@ -0,0 +1,170 @@
<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>