From 7bd7df091bbc00a6a738faa196db7fdaf392c33b Mon Sep 17 00:00:00 2001 From: Baobhan Sith <80159437+Heavrnl@users.noreply.github.com> Date: Tue, 15 Apr 2025 07:39:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=89=8D=E7=AB=AF:=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E6=A0=87=E7=AD=BE=E7=AE=A1=E7=90=86=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/data/nexus-terminal.db | Bin 40960 -> 40960 bytes packages/frontend/src/App.vue | 1 + .../frontend/src/components/AddTagForm.vue | 193 ++++++++++++++++++ packages/frontend/src/components/TagList.vue | 102 +++++++++ packages/frontend/src/locales/en.json | 41 +++- packages/frontend/src/locales/zh.json | 41 +++- packages/frontend/src/router/index.ts | 6 + packages/frontend/src/stores/tags.store.ts | 100 +++++++++ packages/frontend/src/views/TagsView.vue | 103 ++++++++++ 9 files changed, 585 insertions(+), 2 deletions(-) create mode 100644 packages/frontend/src/components/AddTagForm.vue create mode 100644 packages/frontend/src/components/TagList.vue create mode 100644 packages/frontend/src/stores/tags.store.ts create mode 100644 packages/frontend/src/views/TagsView.vue diff --git a/packages/data/nexus-terminal.db b/packages/data/nexus-terminal.db index 378f827809de919f9d65a7df182bee8d9bc75d89..7c37ca6543358f777e399a6e6bfa32beca45b413 100644 GIT binary patch delta 29 lcmZoTz|?SnX@WE(+e8^>Mz)O!?D8zDd(#y*XUk7*004@&33>nk delta 29 lcmZoTz|?SnX@WE((?l6(My8Dk?D8zDe!QBnIa_{e0|1k+3eNxl diff --git a/packages/frontend/src/App.vue b/packages/frontend/src/App.vue index 4311ff8..8c32e7a 100644 --- a/packages/frontend/src/App.vue +++ b/packages/frontend/src/App.vue @@ -20,6 +20,7 @@ const handleLogout = () => { {{ t('nav.dashboard') }} | {{ t('nav.connections') }} | {{ t('nav.proxies') }} | + {{ t('nav.tags') }} | {{ t('nav.login') }} {{ t('nav.logout') }} diff --git a/packages/frontend/src/components/AddTagForm.vue b/packages/frontend/src/components/AddTagForm.vue new file mode 100644 index 0000000..8961f74 --- /dev/null +++ b/packages/frontend/src/components/AddTagForm.vue @@ -0,0 +1,193 @@ + + + + + + {{ formTitle }} + + + {{ t('tags.form.name') }} + + + + + {{ formError || tagsStore.error }} + + + + + {{ submitButtonText }} + + + {{ t('tags.form.cancel') }} + + + + + + + + diff --git a/packages/frontend/src/components/TagList.vue b/packages/frontend/src/components/TagList.vue new file mode 100644 index 0000000..1596c82 --- /dev/null +++ b/packages/frontend/src/components/TagList.vue @@ -0,0 +1,102 @@ + + + + + + + {{ t('tags.table.name') }} + {{ t('tags.table.updatedAt') }} + {{ t('tags.table.actions') }} + + + + + {{ tag.name }} + {{ formatDate(tag.updated_at) }} + + + {{ t('tags.actions.edit') }} + + + {{ t('tags.actions.delete') }} + + + + + + + + diff --git a/packages/frontend/src/locales/en.json b/packages/frontend/src/locales/en.json index 2dff7b3..ea9edb8 100644 --- a/packages/frontend/src/locales/en.json +++ b/packages/frontend/src/locales/en.json @@ -5,7 +5,8 @@ "connections": "Connections", "proxies": "Proxies", "login": "Login", - "logout": "Logout" + "logout": "Logout", + "tags": "Tags" }, "login": { "title": "User Login", @@ -218,5 +219,43 @@ "saving": "Saving", "saveSuccess": "Save successful", "saveError": "Save error" + }, + "tags": { + "title": "Tag Management", + "addTag": "Add New Tag", + "loading": "Loading tags...", + "error": "Failed to load tags: {error}", + "noTags": "No tags yet. Click 'Add New Tag' to create one!", + "table": { + "name": "Name", + "updatedAt": "Updated At", + "actions": "Actions" + }, + "actions": { + "edit": "Edit", + "delete": "Delete" + }, + "form": { + "title": "Add New Tag", + "titleEdit": "Edit Tag", + "name": "Tag Name:", + "confirm": "Confirm Add", + "confirmEdit": "Confirm Edit", + "adding": "Adding...", + "saving": "Saving...", + "cancel": "Cancel", + "errorNameRequired": "Tag name cannot be empty.", + "errorAdd": "Failed to add tag: {error}", + "errorUpdate": "Failed to update tag: {error}" + }, + "prompts": { + "confirmDelete": "Are you sure you want to delete the tag \"{name}\"? This cannot be undone." + }, + "errors": { + "deleteFailed": "Failed to delete tag: {error}" + }, + "status": { + "never": "Never" + } } } diff --git a/packages/frontend/src/locales/zh.json b/packages/frontend/src/locales/zh.json index 8eab0ca..2ed02d5 100644 --- a/packages/frontend/src/locales/zh.json +++ b/packages/frontend/src/locales/zh.json @@ -5,7 +5,8 @@ "connections": "连接管理", "proxies": "代理管理", "login": "登录", - "logout": "登出" + "logout": "登出", + "tags": "标签管理" }, "login": { "title": "用户登录", @@ -221,5 +222,43 @@ "saving": "正在保存", "saveSuccess": "保存成功", "saveError": "保存出错" + }, + "tags": { + "title": "标签管理", + "addTag": "添加新标签", + "loading": "正在加载标签...", + "error": "加载标签列表失败: {error}", + "noTags": "还没有任何标签。点击“添加新标签”来创建一个吧!", + "table": { + "name": "名称", + "updatedAt": "更新时间", + "actions": "操作" + }, + "actions": { + "edit": "编辑", + "delete": "删除" + }, + "form": { + "title": "添加新标签", + "titleEdit": "编辑标签", + "name": "标签名称:", + "confirm": "确认添加", + "confirmEdit": "确认编辑", + "adding": "正在添加...", + "saving": "正在保存...", + "cancel": "取消", + "errorNameRequired": "标签名称不能为空。", + "errorAdd": "添加标签失败: {error}", + "errorUpdate": "更新标签失败: {error}" + }, + "prompts": { + "confirmDelete": "确定要删除标签 \"{name}\" 吗?此操作不可撤销。" + }, + "errors": { + "deleteFailed": "删除标签失败: {error}" + }, + "status": { + "never": "从未" + } } } diff --git a/packages/frontend/src/router/index.ts b/packages/frontend/src/router/index.ts index b0ce005..f066906 100644 --- a/packages/frontend/src/router/index.ts +++ b/packages/frontend/src/router/index.ts @@ -28,6 +28,12 @@ const routes: Array = [ name: 'Proxies', component: () => import('../views/ProxiesView.vue') }, + // 新增:标签管理页面 + { + path: '/tags', + name: 'Tags', + component: () => import('../views/TagsView.vue') + }, // 工作区页面,需要 connectionId 参数 { path: '/workspace/:connectionId', // 使用动态路由段 diff --git a/packages/frontend/src/stores/tags.store.ts b/packages/frontend/src/stores/tags.store.ts new file mode 100644 index 0000000..cfaa2c8 --- /dev/null +++ b/packages/frontend/src/stores/tags.store.ts @@ -0,0 +1,100 @@ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import axios from 'axios'; // 假设使用 axios 发送请求 + +// 定义标签信息接口 +export interface TagInfo { + id: number; + name: string; + created_at: number; + updated_at: number; +} + +export const useTagsStore = defineStore('tags', () => { + const tags = ref([]); + const isLoading = ref(false); + const error = ref(null); + + // 获取标签列表 + async function fetchTags() { + isLoading.value = true; + error.value = null; + try { + const response = await axios.get('/api/v1/tags'); + tags.value = response.data; + return true; + } catch (err: any) { + console.error('Failed to fetch tags:', err); + error.value = err.response?.data?.message || err.message || '获取标签列表失败'; + return false; + } finally { + isLoading.value = false; + } + } + + // 添加新标签 + async function addTag(name: string): Promise { + isLoading.value = true; + error.value = null; + try { + const response = await axios.post<{ message: string, tag: TagInfo }>('/api/v1/tags', { name }); + // 添加成功后,重新获取列表以保证数据同步 (或者直接将新标签添加到 ref) + await fetchTags(); // 简单起见,重新获取 + // tags.value.push(response.data.tag); // 另一种方式 + // tags.value.sort((a, b) => a.name.localeCompare(b.name)); // 保持排序 + return true; + } catch (err: any) { + console.error('Failed to add tag:', err); + error.value = err.response?.data?.message || err.message || '添加标签失败'; + return false; + } finally { + isLoading.value = false; + } + } + + // 更新标签 + async function updateTag(id: number, name: string): Promise { + isLoading.value = true; + error.value = null; + try { + await axios.put(`/api/v1/tags/${id}`, { name }); + // 更新成功后,重新获取列表 + await fetchTags(); + return true; + } catch (err: any) { + console.error('Failed to update tag:', err); + error.value = err.response?.data?.message || err.message || '更新标签失败'; + return false; + } finally { + isLoading.value = false; + } + } + + // 删除标签 + async function deleteTag(id: number): Promise { + isLoading.value = true; + error.value = null; + try { + await axios.delete(`/api/v1/tags/${id}`); + // 删除成功后,重新获取列表 + await fetchTags(); + return true; + } catch (err: any) { + console.error('Failed to delete tag:', err); + error.value = err.response?.data?.message || err.message || '删除标签失败'; + return false; + } finally { + isLoading.value = false; + } + } + + return { + tags, + isLoading, + error, + fetchTags, + addTag, + updateTag, + deleteTag, + }; +}); diff --git a/packages/frontend/src/views/TagsView.vue b/packages/frontend/src/views/TagsView.vue new file mode 100644 index 0000000..685e69d --- /dev/null +++ b/packages/frontend/src/views/TagsView.vue @@ -0,0 +1,103 @@ + + + + + {{ t('tags.title') }} + + + {{ t('tags.addTag') }} + + + + {{ t('tags.loading') }} + + + {{ t('tags.error', { error: tagsStore.error }) }} + + + {{ t('tags.noTags') }} + + + + + + + + +