mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 23:56:42 +08:00
优化识别词应用详情显示 (#481)
This commit is contained in:
@@ -103,11 +103,15 @@ interface EpisodeFormatRecommendData {
|
|||||||
const episodeFormatRecommendState = reactive<{
|
const episodeFormatRecommendState = reactive<{
|
||||||
loading: boolean
|
loading: boolean
|
||||||
ruleName?: string
|
ruleName?: string
|
||||||
|
rulePattern?: string
|
||||||
|
generatedFormat?: string
|
||||||
sampleFile?: string
|
sampleFile?: string
|
||||||
lastMessage?: string
|
lastMessage?: string
|
||||||
}>({
|
}>({
|
||||||
loading: false,
|
loading: false,
|
||||||
ruleName: undefined,
|
ruleName: undefined,
|
||||||
|
rulePattern: undefined,
|
||||||
|
generatedFormat: undefined,
|
||||||
sampleFile: undefined,
|
sampleFile: undefined,
|
||||||
lastMessage: undefined,
|
lastMessage: undefined,
|
||||||
})
|
})
|
||||||
@@ -627,18 +631,99 @@ const previewFileRows = computed(() => {
|
|||||||
|
|
||||||
// 标准化预览项中的识别词命中详情
|
// 标准化预览项中的识别词命中详情
|
||||||
function getPreviewApplyWords(item: ManualTransferPreviewItem) {
|
function getPreviewApplyWords(item: ManualTransferPreviewItem) {
|
||||||
return (item.apply_words ?? []).filter(Boolean)
|
return [
|
||||||
|
...new Set(
|
||||||
|
(item.apply_words ?? [])
|
||||||
|
.map(word => word?.trim())
|
||||||
|
.filter((word): word is string => Boolean(word)),
|
||||||
|
),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 手动整理识别词应用详情
|
// 手动整理识别词应用详情
|
||||||
const previewCustomWordDetails = computed(() => {
|
const previewCustomWordDetails = computed(() => {
|
||||||
return filteredPreviewItems.value
|
const groupedDetails = new Map<string, { sourceNames: string[]; orgString?: string; applyWords: string[] }>()
|
||||||
.map(item => ({
|
|
||||||
sourceName: getFileName(item.source),
|
filteredPreviewItems.value.forEach(item => {
|
||||||
orgString: item.org_string,
|
const applyWords = getPreviewApplyWords(item)
|
||||||
applyWords: getPreviewApplyWords(item),
|
if (!applyWords.length) return
|
||||||
}))
|
|
||||||
.filter(item => item.applyWords.length > 0)
|
const sourceName = getFileName(item.source)
|
||||||
|
const orgString = item.org_string?.trim() || undefined
|
||||||
|
const detailKey = JSON.stringify(applyWords)
|
||||||
|
const existingDetail = groupedDetails.get(detailKey)
|
||||||
|
|
||||||
|
if (existingDetail) {
|
||||||
|
if (!existingDetail.sourceNames.includes(sourceName)) existingDetail.sourceNames.push(sourceName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupedDetails.set(detailKey, {
|
||||||
|
sourceNames: [sourceName],
|
||||||
|
orgString,
|
||||||
|
applyWords,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return [...groupedDetails.values()].map(detail => ({
|
||||||
|
sourceName:
|
||||||
|
detail.sourceNames.length > 1
|
||||||
|
? t('dialog.reorganize.customWordsSameRules', { count: detail.sourceNames.length })
|
||||||
|
: detail.sourceNames[0],
|
||||||
|
orgString: detail.sourceNames.length > 1 ? undefined : detail.orgString,
|
||||||
|
applyWords: detail.applyWords,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
const previewEpisodeFormatRuleDetails = computed(() => {
|
||||||
|
const episodeFormat = transferForm.episode_format?.trim()
|
||||||
|
if (!episodeFormat) return []
|
||||||
|
|
||||||
|
const rulePattern = episodeFormatRecommendState.rulePattern?.trim()
|
||||||
|
const isGeneratedEpisodeFormat =
|
||||||
|
Boolean(episodeFormatRecommendState.generatedFormat) &&
|
||||||
|
episodeFormatRecommendState.generatedFormat === episodeFormat
|
||||||
|
|
||||||
|
if (!isGeneratedEpisodeFormat || !episodeFormatRecommendState.ruleName) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
sourceName: t('dialog.reorganize.episodeFormatManualInput'),
|
||||||
|
orgString: t('dialog.reorganize.episodeFormatFinal', {
|
||||||
|
format: episodeFormat,
|
||||||
|
}),
|
||||||
|
applyWords: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
sourceName: t('dialog.reorganize.episodeFormatRecommendRule', {
|
||||||
|
rule: episodeFormatRecommendState.ruleName,
|
||||||
|
}),
|
||||||
|
orgString: t('dialog.reorganize.episodeFormatFinal', {
|
||||||
|
format: episodeFormat,
|
||||||
|
}),
|
||||||
|
applyWords: rulePattern
|
||||||
|
? [
|
||||||
|
t('dialog.reorganize.episodeFormatRulePattern', {
|
||||||
|
pattern: rulePattern,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const previewRecognitionDetails = computed(() => [
|
||||||
|
...previewCustomWordDetails.value,
|
||||||
|
...previewEpisodeFormatRuleDetails.value,
|
||||||
|
])
|
||||||
|
|
||||||
|
const previewRecognitionDetailTitle = computed(() => {
|
||||||
|
return previewCustomWordDetails.value.length
|
||||||
|
? t('dialog.reorganize.customWordsApplied')
|
||||||
|
: t('dialog.reorganize.episodeFormatRuleDetails')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否需要拓宽窗口
|
// 是否需要拓宽窗口
|
||||||
@@ -700,6 +785,12 @@ const canRecommendEpisodeFormat = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const episodeFormatRecommendSelectionKey = computed(() => {
|
||||||
|
const sourceItem = episodeFormatRecommendSourceItem.value
|
||||||
|
if (sourceItem) return getFileItemKey(sourceItem)
|
||||||
|
return episodeFormatRecommendSelectedFileItems.value.map(item => getFileItemKey(item)).join('||')
|
||||||
|
})
|
||||||
|
|
||||||
const episodeFormatRecommendTooltip = computed(() => {
|
const episodeFormatRecommendTooltip = computed(() => {
|
||||||
if (episodeFormatRecommendState.loading) return t('dialog.reorganize.episodeFormatRecommendLoading')
|
if (episodeFormatRecommendState.loading) return t('dialog.reorganize.episodeFormatRecommendLoading')
|
||||||
if (
|
if (
|
||||||
@@ -716,14 +807,14 @@ const episodeFormatRecommendTooltip = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => getFileItemKey(episodeFormatRecommendSourceItem.value),
|
episodeFormatRecommendSelectionKey,
|
||||||
sourceKey => {
|
() => {
|
||||||
transferForm.fileitem = episodeFormatRecommendSourceItem.value ?? ({} as FileItem)
|
transferForm.fileitem = episodeFormatRecommendSourceItem.value ?? ({} as FileItem)
|
||||||
if (!sourceKey) {
|
episodeFormatRecommendState.ruleName = undefined
|
||||||
episodeFormatRecommendState.ruleName = undefined
|
episodeFormatRecommendState.rulePattern = undefined
|
||||||
episodeFormatRecommendState.sampleFile = undefined
|
episodeFormatRecommendState.generatedFormat = undefined
|
||||||
episodeFormatRecommendState.lastMessage = undefined
|
episodeFormatRecommendState.sampleFile = undefined
|
||||||
}
|
episodeFormatRecommendState.lastMessage = undefined
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
@@ -831,6 +922,8 @@ async function handleRecommendEpisodeFormat() {
|
|||||||
|
|
||||||
transferForm.episode_format = data.episode_format
|
transferForm.episode_format = data.episode_format
|
||||||
episodeFormatRecommendState.ruleName = data.rule_name
|
episodeFormatRecommendState.ruleName = data.rule_name
|
||||||
|
episodeFormatRecommendState.rulePattern = data.pattern
|
||||||
|
episodeFormatRecommendState.generatedFormat = data.episode_format
|
||||||
episodeFormatRecommendState.sampleFile = data.sample_file
|
episodeFormatRecommendState.sampleFile = data.sample_file
|
||||||
episodeFormatRecommendState.lastMessage = data.message
|
episodeFormatRecommendState.lastMessage = data.message
|
||||||
|
|
||||||
@@ -1573,14 +1666,14 @@ onUnmounted(() => {
|
|||||||
<span class="preview-overview-card__value">{{ previewEpisodeCountText }}</span>
|
<span class="preview-overview-card__value">{{ previewEpisodeCountText }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="previewCustomWordDetails.length" class="preview-custom-words">
|
<div v-if="previewRecognitionDetails.length" class="preview-custom-words">
|
||||||
<div class="preview-custom-words__title">
|
<div class="preview-custom-words__title">
|
||||||
<VIcon icon="mdi-tag-text-outline" size="16" />
|
<VIcon icon="mdi-tag-text-outline" size="16" />
|
||||||
<span>{{ t('dialog.reorganize.customWordsApplied') }}</span>
|
<span>{{ previewRecognitionDetailTitle }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="preview-custom-words__items">
|
<div class="preview-custom-words__items">
|
||||||
<div
|
<div
|
||||||
v-for="(detail, index) in previewCustomWordDetails"
|
v-for="(detail, index) in previewRecognitionDetails"
|
||||||
:key="`${detail.sourceName}-${index}`"
|
:key="`${detail.sourceName}-${index}`"
|
||||||
class="preview-custom-words__item"
|
class="preview-custom-words__item"
|
||||||
>
|
>
|
||||||
@@ -1588,7 +1681,7 @@ onUnmounted(() => {
|
|||||||
<div v-if="detail.orgString" class="preview-custom-words__original">
|
<div v-if="detail.orgString" class="preview-custom-words__original">
|
||||||
{{ detail.orgString }}
|
{{ detail.orgString }}
|
||||||
</div>
|
</div>
|
||||||
<div class="preview-custom-words__chips">
|
<div v-if="detail.applyWords.length" class="preview-custom-words__chips">
|
||||||
<VChip
|
<VChip
|
||||||
v-for="(word, wordIndex) in detail.applyWords"
|
v-for="(word, wordIndex) in detail.applyWords"
|
||||||
:key="`${word}-${wordIndex}`"
|
:key="`${word}-${wordIndex}`"
|
||||||
|
|||||||
@@ -2614,6 +2614,10 @@ export default {
|
|||||||
episodeFormatRecommendFailed: 'Failed to generate episode format, please try again later',
|
episodeFormatRecommendFailed: 'Failed to generate episode format, please try again later',
|
||||||
episodeFormatRecommendRule: 'Matched Rule: {rule}',
|
episodeFormatRecommendRule: 'Matched Rule: {rule}',
|
||||||
episodeFormatRecommendSample: 'Sample File: {file}',
|
episodeFormatRecommendSample: 'Sample File: {file}',
|
||||||
|
episodeFormatRuleDetails: 'Manual Episode Positioning Rule',
|
||||||
|
episodeFormatFinal: 'Final Episode Positioning: {format}',
|
||||||
|
episodeFormatManualInput: 'Manual Episode Positioning Input',
|
||||||
|
episodeFormatRulePattern: 'Rule Regex: {pattern}',
|
||||||
episodeOffset: 'Episode Offset',
|
episodeOffset: 'Episode Offset',
|
||||||
episodeOffsetHint: 'Episode offset calculation, e.g. -10 or EP*2',
|
episodeOffsetHint: 'Episode offset calculation, e.g. -10 or EP*2',
|
||||||
episodeOffsetPlaceholder: 'e.g. -10',
|
episodeOffsetPlaceholder: 'e.g. -10',
|
||||||
@@ -2644,7 +2648,8 @@ export default {
|
|||||||
previewSeasonInfo: 'Season',
|
previewSeasonInfo: 'Season',
|
||||||
previewSeasonLabel: 'Season',
|
previewSeasonLabel: 'Season',
|
||||||
previewEpisodeCount: 'Episodes',
|
previewEpisodeCount: 'Episodes',
|
||||||
customWordsApplied: 'Recognition Word Details',
|
customWordsApplied: 'Custom Recognition Words Matched',
|
||||||
|
customWordsSameRules: '{count} files applied the same recognition words',
|
||||||
previewAfterColumn: 'After',
|
previewAfterColumn: 'After',
|
||||||
previewBeforeColumn: 'Before',
|
previewBeforeColumn: 'Before',
|
||||||
previewFileNameColumn: 'Filename',
|
previewFileNameColumn: 'Filename',
|
||||||
|
|||||||
@@ -2567,6 +2567,10 @@ export default {
|
|||||||
episodeFormatRecommendFailed: '集数定位生成失败,请稍后重试',
|
episodeFormatRecommendFailed: '集数定位生成失败,请稍后重试',
|
||||||
episodeFormatRecommendRule: '命中规则:{rule}',
|
episodeFormatRecommendRule: '命中规则:{rule}',
|
||||||
episodeFormatRecommendSample: '样本文件:{file}',
|
episodeFormatRecommendSample: '样本文件:{file}',
|
||||||
|
episodeFormatRuleDetails: '手动整理集数定位规则',
|
||||||
|
episodeFormatFinal: '最终集数定位:{format}',
|
||||||
|
episodeFormatManualInput: '手动输入集数定位',
|
||||||
|
episodeFormatRulePattern: '规则正则:{pattern}',
|
||||||
episodeOffset: '集数偏移',
|
episodeOffset: '集数偏移',
|
||||||
episodeOffsetHint: '集数偏移运算,如-10或EP*2',
|
episodeOffsetHint: '集数偏移运算,如-10或EP*2',
|
||||||
episodeOffsetPlaceholder: '如-10',
|
episodeOffsetPlaceholder: '如-10',
|
||||||
@@ -2597,7 +2601,8 @@ export default {
|
|||||||
previewSeasonInfo: '季信息',
|
previewSeasonInfo: '季信息',
|
||||||
previewSeasonLabel: '季',
|
previewSeasonLabel: '季',
|
||||||
previewEpisodeCount: '总集数',
|
previewEpisodeCount: '总集数',
|
||||||
customWordsApplied: '识别词应用详情',
|
customWordsApplied: '自定义识别词匹配中',
|
||||||
|
customWordsSameRules: '{count} 个文件应用相同识别词',
|
||||||
previewAfterColumn: '整理后',
|
previewAfterColumn: '整理后',
|
||||||
previewBeforeColumn: '整理前',
|
previewBeforeColumn: '整理前',
|
||||||
previewFileNameColumn: '文件名',
|
previewFileNameColumn: '文件名',
|
||||||
|
|||||||
@@ -2568,6 +2568,10 @@ export default {
|
|||||||
episodeFormatRecommendFailed: '集數定位生成失敗,請稍後重試',
|
episodeFormatRecommendFailed: '集數定位生成失敗,請稍後重試',
|
||||||
episodeFormatRecommendRule: '命中規則:{rule}',
|
episodeFormatRecommendRule: '命中規則:{rule}',
|
||||||
episodeFormatRecommendSample: '樣本文件:{file}',
|
episodeFormatRecommendSample: '樣本文件:{file}',
|
||||||
|
episodeFormatRuleDetails: '手動整理集數定位規則',
|
||||||
|
episodeFormatFinal: '最終集數定位:{format}',
|
||||||
|
episodeFormatManualInput: '手動輸入集數定位',
|
||||||
|
episodeFormatRulePattern: '規則正則:{pattern}',
|
||||||
episodeOffset: '集數偏移',
|
episodeOffset: '集數偏移',
|
||||||
episodeOffsetHint: '集數偏移運算,如-10或EP*2',
|
episodeOffsetHint: '集數偏移運算,如-10或EP*2',
|
||||||
episodeOffsetPlaceholder: '如-10',
|
episodeOffsetPlaceholder: '如-10',
|
||||||
@@ -2598,7 +2602,8 @@ export default {
|
|||||||
previewSeasonInfo: '季資訊',
|
previewSeasonInfo: '季資訊',
|
||||||
previewSeasonLabel: '季',
|
previewSeasonLabel: '季',
|
||||||
previewEpisodeCount: '總集數',
|
previewEpisodeCount: '總集數',
|
||||||
customWordsApplied: '識別詞應用詳情',
|
customWordsApplied: '自定義識別詞匹配中',
|
||||||
|
customWordsSameRules: '{count} 個文件套用相同識別詞',
|
||||||
previewAfterColumn: '整理後',
|
previewAfterColumn: '整理後',
|
||||||
previewBeforeColumn: '整理前',
|
previewBeforeColumn: '整理前',
|
||||||
previewFileNameColumn: '文件名',
|
previewFileNameColumn: '文件名',
|
||||||
|
|||||||
Reference in New Issue
Block a user