From 712dfa3fe14c6e15213e371592b9b7fc87d09a5f Mon Sep 17 00:00:00 2001 From: jxxghp Date: Fri, 17 Apr 2026 15:02:56 +0800 Subject: [PATCH] feat: improve transfer history footer actions and plugin market settings --- .../dialog/PluginMarketSettingDialog.vue | 190 +++++++++++++++--- src/layouts/components/Footer.vue | 46 ++++- src/locales/en-US.ts | 7 + src/locales/zh-CN.ts | 7 + src/locales/zh-TW.ts | 7 + src/views/reorganize/TransferHistoryView.vue | 132 +++++++++--- 6 files changed, 320 insertions(+), 69 deletions(-) diff --git a/src/components/dialog/PluginMarketSettingDialog.vue b/src/components/dialog/PluginMarketSettingDialog.vue index 8e4ffaa9..ae391765 100644 --- a/src/components/dialog/PluginMarketSettingDialog.vue +++ b/src/components/dialog/PluginMarketSettingDialog.vue @@ -2,50 +2,34 @@ import api from '@/api' import { useToast } from 'vue-toastification' import { useI18n } from 'vue-i18n' -import { computed } from 'vue' import { useDisplay } from 'vuetify' -// 显示器宽度 const display = useDisplay() -// 国际化 const { t } = useI18n() const $toast = useToast() -// 插件仓库设置字符串 -const repoString = ref('') -// 用于显示的仓库地址数组 -const repoArray = ref([]) +const repoList = ref([]) +const newRepoUrl = ref('') +const editingIndex = ref(null) +const editingUrl = ref('') -// 计算属性:在数组和换行符分隔的字符串之间转换 -const displayRepos = computed({ - get: () => repoArray.value.join('\n'), - set: (value: string) => { - repoArray.value = value.split('\n').filter((repo: string) => repo.trim() !== '') - }, -}) - -// 定义事件 const emit = defineEmits(['save', 'close']) -// 查询已设置的插件仓库 async function queryMarketRepoSetting() { try { const result: { [key: string]: any } = await api.get('system/setting/PLUGIN_MARKET') if (result && result.data && result.data.value) { - repoString.value = result.data.value - repoArray.value = result.data.value.split(',').filter((repo: string) => repo.trim() !== '') + repoList.value = result.data.value.split(',').filter((repo: string) => repo.trim() !== '') } } catch (error) { console.log(error) } } -// 保存设置 async function saveHandle() { try { - // 将数组转换为逗号分隔的字符串 - const repoStringToSave = repoArray.value.join(',') + const repoStringToSave = repoList.value.join(',') const result: { [key: string]: any } = await api.post('system/setting/PLUGIN_MARKET', repoStringToSave) if (result.success) { @@ -57,6 +41,68 @@ async function saveHandle() { } } +function addRepo() { + const url = newRepoUrl.value.trim() + if (!url) return + + if (!url.startsWith('http://') && !url.startsWith('https://')) { + $toast.error(t('dialog.pluginMarketSetting.invalidUrl')) + return + } + + if (repoList.value.includes(url)) { + $toast.error(t('dialog.pluginMarketSetting.duplicateUrl')) + return + } + + repoList.value.push(url) + newRepoUrl.value = '' +} + +function removeRepo(index: number) { + repoList.value.splice(index, 1) +} + +function startEdit(index: number) { + editingIndex.value = index + editingUrl.value = repoList.value[index] +} + +function saveEdit() { + if (editingIndex.value === null) return + + const url = editingUrl.value.trim() + if (!url) return + + if (!url.startsWith('http://') && !url.startsWith('https://')) { + $toast.error(t('dialog.pluginMarketSetting.invalidUrl')) + return + } + + repoList.value[editingIndex.value] = url + editingIndex.value = null + editingUrl.value = '' +} + +function cancelEdit() { + editingIndex.value = null + editingUrl.value = '' +} + +function moveUp(index: number) { + if (index === 0) return + const temp = repoList.value[index] + repoList.value[index] = repoList.value[index - 1] + repoList.value[index - 1] = temp +} + +function moveDown(index: number) { + if (index === repoList.value.length - 1) return + const temp = repoList.value[index] + repoList.value[index] = repoList.value[index + 1] + repoList.value[index + 1] = temp +} + onMounted(() => { queryMarketRepoSetting() }) @@ -64,7 +110,7 @@ onMounted(() => { + + diff --git a/src/layouts/components/Footer.vue b/src/layouts/components/Footer.vue index ee1fece8..b2773ccd 100644 --- a/src/layouts/components/Footer.vue +++ b/src/layouts/components/Footer.vue @@ -120,6 +120,12 @@ interface DynamicButton { action: () => void show: boolean routePath?: string // 添加路径属性,用于标识哪个路由注册的 + menuItems?: { + title: string + icon?: string + color?: string + action: () => void + }[] } // 提供动态按钮注册和获取的方法 @@ -146,6 +152,7 @@ if (typeof window !== 'undefined') { // 提供给其他组件使用 provide('registerDynamicButton', registerDynamicButton) provide('unregisterDynamicButton', unregisterDynamicButton) +provide('dynamicButton', dynamicButton) // 在组件销毁时清理 onUnmounted(() => { @@ -165,6 +172,8 @@ const showDynamicButton = computed(() => { (!dynamicButton.value.routePath || dynamicButton.value.routePath === route.path) ) }) + +const hasDynamicButtonMenu = computed(() => Boolean(dynamicButton.value?.menuItems?.length))