feat: 后端 & 前端: 实现连接与标签的关联管理

This commit is contained in:
Baobhan Sith
2025-04-15 07:46:57 +08:00
parent 7bd7df091b
commit 6cd4977347
8 changed files with 268 additions and 39 deletions
@@ -1,24 +1,47 @@
<script setup lang="ts">
import { onMounted } from 'vue';
import { onMounted, computed } from 'vue'; // 引入 computed
import { storeToRefs } from 'pinia';
import { useRouter } from 'vue-router'; // 引入 useRouter
import { useI18n } from 'vue-i18n'; // 引入 useI18n
import { useConnectionsStore, ConnectionInfo } from '../stores/connections.store'; // 引入 ConnectionInfo 类型
import { useTagsStore } from '../stores/tags.store'; // 引入 Tags Store
const { t } = useI18n(); // 获取 t 函数
const router = useRouter(); // 获取 router 实例
const connectionsStore = useConnectionsStore();
const tagsStore = useTagsStore(); // 获取 Tags Store 实例
// 使用 storeToRefs 来保持 state 属性的响应性
const { connections, isLoading, error } = storeToRefs(connectionsStore);
const { tags: allTags } = storeToRefs(tagsStore); // 获取所有标签
// 定义组件发出的事件 (添加 edit-connection)
const emit = defineEmits(['edit-connection']);
// 组件挂载时获取连接列表
// 组件挂载时获取连接和标签列表
onMounted(() => {
connectionsStore.fetchConnections();
tagsStore.fetchTags(); // 获取标签列表
});
// 创建标签 ID 到名称的映射
const tagMap = computed(() => {
const map = new Map<number, string>();
allTags.value.forEach(tag => {
map.set(tag.id, tag.name);
});
return map;
});
// 获取连接的标签名称数组
const getConnectionTagNames = (conn: ConnectionInfo): string[] => {
if (!conn.tag_ids || conn.tag_ids.length === 0) {
return [];
}
return conn.tag_ids
.map(tagId => tagMap.value.get(tagId)) // 使用映射获取名称
.filter((name): name is string => !!name); // 过滤掉未找到的标签并确保类型为 string
};
// 辅助函数:格式化时间戳
const formatTimestamp = (timestamp: number | null): string => {
if (!timestamp) return t('connections.status.never'); // 使用 i18n
@@ -59,6 +82,7 @@ const handleDelete = async (conn: ConnectionInfo) => {
<th>{{ t('connections.table.port') }}</th>
<th>{{ t('connections.table.user') }}</th>
<th>{{ t('connections.table.authMethod') }}</th>
<th>{{ t('connections.table.tags') }}</th> <!-- 新增标签列 -->
<th>{{ t('connections.table.lastConnected') }}</th>
<th>{{ t('connections.table.actions') }}</th>
</tr>
@@ -70,6 +94,14 @@ const handleDelete = async (conn: ConnectionInfo) => {
<td>{{ conn.port }}</td>
<td>{{ conn.username }}</td>
<td>{{ conn.auth_method }}</td>
<td> <!-- 显示标签 -->
<span v-if="getConnectionTagNames(conn).length > 0" class="tag-list">
<span v-for="tagName in getConnectionTagNames(conn)" :key="tagName" class="tag-item">
{{ tagName }}
</span>
</span>
<span v-else class="no-tags">-</span>
</td>
<td>{{ formatTimestamp(conn.last_connected_at) }}</td>
<td>
<button @click="connectToServer(conn.id)">{{ t('connections.actions.connect') }}</button>
@@ -137,4 +169,23 @@ button {
padding: 0.2rem 0.5rem;
cursor: pointer;
}
/* 标签样式 */
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 0.3rem;
}
.tag-item {
background-color: #e0e0e0;
color: #333;
padding: 0.1rem 0.4rem;
border-radius: 3px;
font-size: 0.85em;
white-space: nowrap;
}
.no-tags {
color: #999;
font-style: italic;
}
</style>