mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-16 21:20:45 +08:00
feat: 添加插件更新历史功能及相关国际化支持
This commit is contained in:
@@ -3,7 +3,6 @@ import { useToast } from 'vue-toastification'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import api from '@/api'
|
||||
import type { Plugin } from '@/api/types'
|
||||
import { isNullOrEmptyObject } from '@core/utils'
|
||||
import { getLogoUrl } from '@/utils/imageUtils'
|
||||
import { getDominantColor } from '@/@core/utils/image'
|
||||
import { formatDownloadCount } from '@/@core/utils/formatters'
|
||||
@@ -104,17 +103,12 @@ async function imageLoaded() {
|
||||
|
||||
// 显示更新日志
|
||||
function showUpdateHistory() {
|
||||
// 检查当前版本是否有更新日志
|
||||
if (isNullOrEmptyObject(props.plugin?.history)) {
|
||||
updatePlugin()
|
||||
} else {
|
||||
openSharedDialog(
|
||||
PluginVersionHistoryDialog,
|
||||
{ plugin: props.plugin, showUpdateAction: true },
|
||||
{ update: updatePlugin },
|
||||
{ closeOn: ['close', 'update', 'update:modelValue'] },
|
||||
)
|
||||
}
|
||||
openSharedDialog(
|
||||
PluginVersionHistoryDialog,
|
||||
{ plugin: props.plugin, showUpdateAction: true },
|
||||
{ update: updatePlugin },
|
||||
{ closeOn: ['close', 'update', 'update:modelValue'] },
|
||||
)
|
||||
}
|
||||
|
||||
// 调用API卸载插件
|
||||
@@ -377,6 +371,15 @@ const dropdownItems = ref([
|
||||
props: {
|
||||
prependIcon: 'mdi-arrow-up-circle-outline',
|
||||
color: 'success',
|
||||
click: updatePlugin,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('plugin.updateHistory'),
|
||||
value: 9,
|
||||
show: !props.plugin?.has_update,
|
||||
props: {
|
||||
prependIcon: 'mdi-update',
|
||||
click: showUpdateHistory,
|
||||
},
|
||||
},
|
||||
@@ -428,6 +431,9 @@ watch(
|
||||
(newHasUpdate, _) => {
|
||||
const updateItemIndex = dropdownItems.value.findIndex(item => item.value === 3)
|
||||
if (updateItemIndex !== -1) dropdownItems.value[updateItemIndex].show = newHasUpdate
|
||||
|
||||
const updateHistoryItemIndex = dropdownItems.value.findIndex(item => item.value === 9)
|
||||
if (updateHistoryItemIndex !== -1) dropdownItems.value[updateHistoryItemIndex].show = !newHasUpdate
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import api from '@/api'
|
||||
import type { Plugin } from '@/api/types'
|
||||
import VersionHistory from '@/components/misc/VersionHistory.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -25,6 +26,10 @@ const props = defineProps({
|
||||
// 定义触发的自定义事件
|
||||
const emit = defineEmits(['update:modelValue', 'close', 'update'])
|
||||
|
||||
const loading = ref(false)
|
||||
const loadError = ref('')
|
||||
const pluginDetail = ref<Plugin | null>(null)
|
||||
|
||||
// 弹窗显示状态
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
@@ -34,30 +39,78 @@ const visible = computed({
|
||||
},
|
||||
})
|
||||
|
||||
const resolvedPlugin = computed(() => pluginDetail.value ?? props.plugin)
|
||||
|
||||
const resolvedHistory = computed(() => resolvedPlugin.value?.history || {})
|
||||
|
||||
const hasHistory = computed(() => Object.keys(resolvedHistory.value).length > 0)
|
||||
|
||||
async function loadPluginHistory() {
|
||||
if (!props.plugin?.id) {
|
||||
pluginDetail.value = null
|
||||
loadError.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
loadError.value = ''
|
||||
|
||||
try {
|
||||
pluginDetail.value = await api.get(`plugin/history/${props.plugin.id}`, {
|
||||
params: {
|
||||
force: true,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
pluginDetail.value = null
|
||||
loadError.value = t('plugin.updateHistoryLoadFailed')
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 触发插件更新操作。 */
|
||||
function handleUpdate() {
|
||||
emit('update')
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [visible.value, props.plugin?.id],
|
||||
([isVisible]) => {
|
||||
if (isVisible) loadPluginHistory()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog v-if="visible" v-model="visible" width="600" max-height="85vh" scrollable>
|
||||
<VCard :title="t('plugin.updateHistoryTitle', { name: props.plugin?.plugin_name })">
|
||||
<VCard :title="t('plugin.updateHistoryTitle', { name: resolvedPlugin?.plugin_name })">
|
||||
<VDialogCloseBtn v-model="visible" />
|
||||
<VDivider />
|
||||
<VersionHistory :history="props.plugin?.history" />
|
||||
<div v-if="loading" class="plugin-version-history-dialog__loading">
|
||||
<VProgressCircular indeterminate color="primary" />
|
||||
</div>
|
||||
<VCardText v-else-if="loadError && !hasHistory">
|
||||
<VAlert type="warning" variant="tonal" density="compact" :text="loadError" />
|
||||
</VCardText>
|
||||
<VCardText v-else-if="!hasHistory">
|
||||
<VAlert type="info" variant="tonal" density="compact" :text="t('plugin.updateHistoryEmpty')" />
|
||||
</VCardText>
|
||||
<VersionHistory v-else :history="resolvedHistory" />
|
||||
<template v-if="props.showUpdateAction">
|
||||
<VDivider />
|
||||
<VCardItem>
|
||||
<VAlert
|
||||
v-if="props.plugin?.system_version_compatible === false"
|
||||
v-if="resolvedPlugin?.system_version_compatible === false"
|
||||
type="warning"
|
||||
variant="tonal"
|
||||
density="compact"
|
||||
class="mb-3"
|
||||
:text="props.plugin?.system_version_message || t('plugin.incompatibleSystemVersion')"
|
||||
:text="resolvedPlugin?.system_version_message || t('plugin.incompatibleSystemVersion')"
|
||||
/>
|
||||
<VBtn @click="handleUpdate" block :disabled="props.plugin?.system_version_compatible === false">
|
||||
<VBtn @click="handleUpdate" block :disabled="resolvedPlugin?.system_version_compatible === false">
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-arrow-up-circle-outline" />
|
||||
</template>
|
||||
@@ -68,3 +121,12 @@ function handleUpdate() {
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.plugin-version-history-dialog__loading {
|
||||
min-height: 12rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2918,6 +2918,8 @@ export default {
|
||||
resetSuccess: 'Plugin {name} data has been reset',
|
||||
resetFailed: 'Plugin {name} reset failed: {message}',
|
||||
updateHistoryTitle: '{name} Update History',
|
||||
updateHistoryEmpty: 'No update history available yet',
|
||||
updateHistoryLoadFailed: 'Failed to load update history. Please try again later.',
|
||||
updateToLatest: 'Update to Latest Version',
|
||||
updatingTo: 'Updating {name} to v{version} ...',
|
||||
folderNameEmpty: 'Folder name cannot be empty',
|
||||
|
||||
@@ -2870,6 +2870,8 @@ export default {
|
||||
resetSuccess: '插件 {name} 数据已重置',
|
||||
resetFailed: '插件 {name} 重置失败:{message}',
|
||||
updateHistoryTitle: '{name} 更新说明',
|
||||
updateHistoryEmpty: '暂未获取到更新说明',
|
||||
updateHistoryLoadFailed: '读取更新说明失败,请稍后重试',
|
||||
updateToLatest: '更新到最新版本',
|
||||
updatingTo: '更新 {name} 到 {version} 版本...',
|
||||
folderNameEmpty: '文件夹名称不能为空',
|
||||
|
||||
@@ -2869,6 +2869,8 @@ export default {
|
||||
resetSuccess: '插件 {name} 數據已重置',
|
||||
resetFailed: '插件 {name} 重置失敗:{message}',
|
||||
updateHistoryTitle: '{name} 更新說明',
|
||||
updateHistoryEmpty: '暫未獲取到更新說明',
|
||||
updateHistoryLoadFailed: '讀取更新說明失敗,請稍後重試',
|
||||
updateToLatest: '更新到最新版本',
|
||||
updatingTo: '正在更新 {name} 至 v{version} ...',
|
||||
folderNameEmpty: '文件夾名稱不能為空',
|
||||
|
||||
Reference in New Issue
Block a user