mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 03:55:09 +08:00
135 lines
4.6 KiB
Vue
135 lines
4.6 KiB
Vue
<template>
|
|
<div class="graph-toolbar">
|
|
<div class="graph-stats">
|
|
<span class="stat-item">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
<circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="10" stroke-width="1.5"/>
|
|
</svg>
|
|
{{ nodeCount }} {{ t('wiki.graph.nodes') }}
|
|
</span>
|
|
<span class="stat-item">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="5" y1="12" x2="19" y2="12"/>
|
|
</svg>
|
|
{{ edgeCount }} {{ t('wiki.graph.edges') }}
|
|
</span>
|
|
<span v-if="orphanCount > 0" class="stat-item stat-warn">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
</svg>
|
|
{{ orphanCount }} {{ t('wiki.graph.orphans') }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="graph-controls">
|
|
<label class="filter-label">
|
|
<input :checked="showOrphans" type="checkbox" @change="emit('update:showOrphans', ($event.target as HTMLInputElement).checked)" />
|
|
{{ t('wiki.graph.showOrphans') }}
|
|
</label>
|
|
<select :value="typeFilter" class="type-select" @change="emit('update:typeFilter', ($event.target as HTMLSelectElement).value)">
|
|
<option value="">{{ t('wiki.graph.allTypes') }}</option>
|
|
<option v-for="type in availableTypes" :key="type" :value="type">
|
|
{{ t(`wiki.pageTypes.${type}`, type) }}
|
|
</option>
|
|
</select>
|
|
<button class="btn-icon-sm" :title="t('wiki.graph.resetView')" @click="emit('reset')">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<polyline points="23 4 23 10 17 10"/>
|
|
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="btn-icon-sm"
|
|
:title="isFullscreen ? t('wiki.graph.exitFullscreen') : t('wiki.graph.fullscreen')"
|
|
@click="emit('toggleFullscreen')"
|
|
>
|
|
<!-- Enter fullscreen icon -->
|
|
<svg v-if="!isFullscreen" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/>
|
|
<line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/>
|
|
</svg>
|
|
<!-- Exit fullscreen icon -->
|
|
<svg v-else width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/>
|
|
<line x1="10" y1="14" x2="3" y2="21"/><line x1="21" y1="3" x2="14" y2="10"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
const { t } = useI18n()
|
|
|
|
defineProps<{
|
|
nodeCount: number
|
|
edgeCount: number
|
|
orphanCount: number
|
|
showOrphans: boolean
|
|
typeFilter: string
|
|
availableTypes: string[]
|
|
isFullscreen: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:showOrphans', val: boolean): void
|
|
(e: 'update:typeFilter', val: string): void
|
|
(e: 'reset'): void
|
|
(e: 'toggleFullscreen'): void
|
|
}>()
|
|
</script>
|
|
|
|
<style scoped>
|
|
.graph-toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 8px 12px;
|
|
border-bottom: 1px solid var(--mc-border-light);
|
|
gap: 12px;
|
|
flex-shrink: 0;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.graph-stats { display: flex; align-items: center; gap: 12px; }
|
|
.stat-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-size: 11px;
|
|
color: var(--mc-text-tertiary);
|
|
}
|
|
.stat-warn { color: var(--mc-danger, #f56c6c); }
|
|
|
|
.graph-controls { display: flex; align-items: center; gap: 8px; }
|
|
.filter-label { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--mc-text-secondary); cursor: pointer; }
|
|
|
|
.type-select {
|
|
padding: 3px 8px;
|
|
font-size: 11px;
|
|
border: 1px solid var(--mc-border-light);
|
|
border-radius: 7px;
|
|
background: var(--mc-bg-elevated);
|
|
color: var(--mc-text-primary);
|
|
cursor: pointer;
|
|
outline: none;
|
|
}
|
|
|
|
.btn-icon-sm {
|
|
width: 26px;
|
|
height: 26px;
|
|
border: 1px solid var(--mc-border-light);
|
|
background: var(--mc-bg-elevated);
|
|
border-radius: 7px;
|
|
cursor: pointer;
|
|
color: var(--mc-text-secondary);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: all 0.15s;
|
|
}
|
|
.btn-icon-sm:hover { background: var(--mc-bg-sunken); color: var(--mc-primary); }
|
|
</style>
|