From de3523056ab9e1d38a7ac112161e0ce6a4a89a4e Mon Sep 17 00:00:00 2001 From: Album <51018113+Mister-album@users.noreply.github.com> Date: Tue, 19 May 2026 07:20:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E6=95=B4=E7=90=86=E9=9B=86=E6=95=B0=E5=AE=9A=E4=BD=8D=E8=A7=84?= =?UTF-8?q?=E5=88=99=E9=85=8D=E7=BD=AE=E5=B9=B6=E6=94=AF=E6=8C=81=E6=99=BA?= =?UTF-8?q?=E8=83=BD=E7=94=9F=E6=88=90=E9=9B=86=E6=95=B0=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=20(#473)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/dialog/ReorganizeDialog.vue | 163 ++++++++++++- src/locales/en-US.ts | 36 ++- src/locales/zh-CN.ts | 32 +++ src/locales/zh-TW.ts | 32 +++ src/views/system/WordsView.vue | 267 +++++++++++++++++++++ 5 files changed, 521 insertions(+), 9 deletions(-) diff --git a/src/components/dialog/ReorganizeDialog.vue b/src/components/dialog/ReorganizeDialog.vue index 38e4949e..0904cce7 100644 --- a/src/components/dialog/ReorganizeDialog.vue +++ b/src/components/dialog/ReorganizeDialog.vue @@ -89,6 +89,30 @@ const previewLoaded = ref(false) // 预览数据 const previewData = ref() +interface EpisodeFormatRecommendData { + rule_name?: string + rule_index?: number + pattern?: string + episode_format?: string + sample_file?: string + min_file_size_mb?: number + message?: string +} + +const episodeFormatRecommendState = reactive<{ + loading: boolean + ruleName?: string + sampleFile?: string + lastMessage?: string +}>({ + loading: false, + ruleName: undefined, + sampleFile: undefined, + lastMessage: undefined, +}) + +const episodeFormatRuleConfigured = ref(undefined) + function getFileItemKey(item?: FileItem) { return [item?.storage ?? '', item?.type ?? '', item?.path ?? ''].join('|') } @@ -414,6 +438,40 @@ const previewToggleIcon = computed(() => { return previewVisible.value ? 'mdi-eye-off-outline' : 'mdi-eye-outline' }) +const episodeFormatRecommendSourceItem = computed(() => { + if (transferForm.fileitem?.path) return transferForm.fileitem + if (normalizedItems.value.length !== 1) return undefined + return normalizedItems.value[0] +}) + +const canRecommendEpisodeFormat = computed(() => { + return ( + Boolean(episodeFormatRecommendSourceItem.value?.path) && + !progressDialog.value && + !episodeFormatRecommendState.loading + ) +}) + +const episodeFormatRecommendTooltip = computed(() => { + if (episodeFormatRecommendState.loading) return t('dialog.reorganize.episodeFormatRecommendLoading') + if (!episodeFormatRecommendSourceItem.value?.path) return t('dialog.reorganize.episodeFormatRecommendSelectFile') + if (episodeFormatRuleConfigured.value === false) return t('dialog.reorganize.episodeFormatRecommendNeedWords') + return t('dialog.reorganize.episodeFormatRecommendAction') +}) + +watch( + () => getFileItemKey(episodeFormatRecommendSourceItem.value), + sourceKey => { + transferForm.fileitem = episodeFormatRecommendSourceItem.value ?? ({} as FileItem) + if (!sourceKey) { + episodeFormatRecommendState.ruleName = undefined + episodeFormatRecommendState.sampleFile = undefined + episodeFormatRecommendState.lastMessage = undefined + } + }, + { immediate: true }, +) + // 构造整理请求 function createTransferPayload(options: { item?: FileItem; logid?: number; preview?: boolean }) { const payload: ManualTransferPayload = { @@ -434,6 +492,65 @@ async function requestManualTransfer( return await api.post(`transfer/manual?background=${background}`, payload) } +async function loadEpisodeFormatRuleConfiguration() { + try { + const result: { [key: string]: any } = await api.get('system/setting/EpisodeFormatRuleTable') + episodeFormatRuleConfigured.value = Boolean(result.data?.value?.length) + } catch (error) { + console.log(error) + episodeFormatRuleConfigured.value = undefined + } +} + +async function handleRecommendEpisodeFormat() { + const sourceItem = episodeFormatRecommendSourceItem.value + if (!sourceItem?.path) { + $toast.warning(t('dialog.reorganize.episodeFormatRecommendSelectFile')) + return + } + + if (episodeFormatRuleConfigured.value === false) { + $toast.warning(t('dialog.reorganize.episodeFormatRecommendNeedWords')) + return + } + + episodeFormatRecommendState.loading = true + + try { + const hasExistingEpisodeFormat = Boolean(transferForm.episode_format?.trim()) + const result = await api.post('transfer/episode-format/recommend', { + fileitem: sourceItem, + }) + + if (!result.success) { + $toast.error(result.message || t('dialog.reorganize.episodeFormatRecommendFailed')) + return + } + + const data = (result.data ?? {}) as EpisodeFormatRecommendData + if (!data.episode_format) { + $toast.error(t('dialog.reorganize.episodeFormatRecommendFailed')) + return + } + + transferForm.episode_format = data.episode_format + episodeFormatRecommendState.ruleName = data.rule_name + episodeFormatRecommendState.sampleFile = data.sample_file + episodeFormatRecommendState.lastMessage = data.message + + $toast.success( + hasExistingEpisodeFormat + ? t('dialog.reorganize.episodeFormatRecommendOverwriteSuccess') + : t('dialog.reorganize.episodeFormatRecommendSuccess'), + ) + } catch (error: any) { + console.log(error) + $toast.error(error?.message || t('dialog.reorganize.episodeFormatRecommendFailed')) + } finally { + episodeFormatRecommendState.loading = false + } +} + // 默认预览数据 function getDefaultPreviewData(): ManualTransferPreviewData { return { @@ -769,6 +886,7 @@ async function transfer(background: boolean = false) { onMounted(() => { loadDirectories() loadStorages() + loadEpisodeFormatRuleConfiguration() }) onUnmounted(() => { @@ -778,8 +896,15 @@ onUnmounted(() => { + +