mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-08 07:51:37 +08:00
添加下载器和媒体服务器选项,重构相关组件以支持新功能,并更新国际化文本以提升用户体验
This commit is contained in:
@@ -33,8 +33,8 @@ const isCollapsed = ref(true)
|
||||
// 类型下拉字典
|
||||
const typeItems = computed(() => [
|
||||
{ title: t('common.all'), value: '' },
|
||||
{ title: t('media.movie'), value: '电影' },
|
||||
{ title: t('media.tv'), value: '电视剧' },
|
||||
{ title: t('mediaType.movie'), value: '电影' },
|
||||
{ title: t('mediaType.tv'), value: '电视剧' },
|
||||
])
|
||||
|
||||
// 计算资源存储字典(整理方式为下载器时不能为远程存储)
|
||||
|
||||
@@ -9,6 +9,7 @@ import transmission_image from '@images/logos/transmission.png'
|
||||
import custom_image from '@images/logos/downloader.png'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { downloaderDict } from '@/api/constants'
|
||||
|
||||
// 获取i18n实例
|
||||
const { t } = useI18n()
|
||||
@@ -173,11 +174,11 @@ onUnmounted(() => {
|
||||
/>
|
||||
<span class="text-h6">{{ downloader.name }}</span>
|
||||
</div>
|
||||
<div v-if="downloader.type != 'custom' && props.downloader.enabled" class="mt-1 flex flex-wrap text-sm">
|
||||
<div v-if="downloaderDict[downloader.type] && props.downloader.enabled" class="mt-1 flex flex-wrap text-sm">
|
||||
<span class="me-2">{{ `↑ ${formatFileSize(upload_rate, 1)}/s ` }}</span>
|
||||
<span>{{ `↓ ${formatFileSize(download_rate, 1)}/s` }}</span>
|
||||
</div>
|
||||
<div v-else-if="downloader.type == 'custom'" class="mt-1 flex flex-wrap text-sm">
|
||||
<div v-else-if="!downloaderDict[downloader.type]" class="mt-1 flex flex-wrap text-sm">
|
||||
<span class="me-2">自定义下载器</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -324,11 +325,20 @@ onUnmounted(() => {
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-else>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="downloaderInfo.type"
|
||||
:label="t('downloader.type')"
|
||||
:hint="t('downloader.customTypeHint')"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="downloaderInfo.name"
|
||||
:label="t('downloader.name')"
|
||||
:placeholder="t('downloader.nameRequired')"
|
||||
:hint="t('downloader.nameRequired')"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
|
||||
@@ -9,6 +9,7 @@ import custom_image from '@images/logos/mediaserver.png'
|
||||
import api from '@/api'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { mediaServerDict } from '@/api/constants'
|
||||
|
||||
// 获取i18n实例
|
||||
const { t } = useI18n()
|
||||
@@ -186,12 +187,12 @@ onMounted(() => {
|
||||
<VCardText class="flex justify-space-between align-center gap-3">
|
||||
<div class="align-self-start flex-1">
|
||||
<div class="text-h6 mb-1">{{ mediaserver.name }}</div>
|
||||
<div v-if="mediaserver.type != 'custom' && mediaserver.enabled" class="text-sm mt-5 flex flex-wrap">
|
||||
<div v-if="mediaServerDict[mediaserver.type] && mediaserver.enabled" class="text-sm mt-5 flex flex-wrap">
|
||||
<span v-for="item in infoItems" :key="item.title" class="me-2 mb-1">
|
||||
<VIcon rounded :icon="item.avatar" class="me-1" />{{ item.amount }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-else-if="mediaserver.type == 'custom'" class="text-sm mt-5 flex flex-wrap">
|
||||
<div v-else-if="!mediaServerDict[mediaserver.type]" class="text-sm mt-5 flex flex-wrap">
|
||||
<span class="me-2 mb-1">自定义媒体服务器</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -453,11 +454,15 @@ onMounted(() => {
|
||||
<VRow v-else>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="mediaServerInfo.name"
|
||||
:label="t('common.name')"
|
||||
:placeholder="t('mediaserver.nameRequired')"
|
||||
v-model="mediaServerInfo.type"
|
||||
:label="t('mediaserver.type')"
|
||||
:hint="t('mediaserver.customTypeHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField :label="t('common.name')" :hint="t('mediaserver.nameRequired')" persistent-hint />
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
|
||||
@@ -15,6 +15,7 @@ import AlistConfigDialog from '../dialog/AlistConfigDialog.vue'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import { isNullOrEmptyObject } from '@/@core/utils'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { storageIconDict } from '@/api/constants'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
@@ -50,6 +51,9 @@ const storage_ref = ref(props.storage)
|
||||
// 自定义存储名称
|
||||
const customName = ref(props.storage.name)
|
||||
|
||||
// 自定义存储类型
|
||||
const storageType = ref(props.storage.type)
|
||||
|
||||
// 阿里云盘认证对话框
|
||||
const aliyunAuthDialog = ref(false)
|
||||
// 115网盘认证对话框
|
||||
@@ -76,11 +80,11 @@ function openStorageDialog() {
|
||||
case 'alist':
|
||||
aListConfigDialog.value = true
|
||||
break
|
||||
case 'custom':
|
||||
customConfigDialog.value = true
|
||||
case 'local':
|
||||
$toast.info(t('storage.noConfigNeeded'))
|
||||
break
|
||||
default:
|
||||
$toast.info(t('storage.noConfigNeeded'))
|
||||
customConfigDialog.value = true
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -98,10 +102,8 @@ const getIcon = computed(() => {
|
||||
return rclone_png
|
||||
case 'alist':
|
||||
return alist_png
|
||||
case 'custom':
|
||||
return custom_png
|
||||
default:
|
||||
return storage_png
|
||||
return custom_png
|
||||
}
|
||||
})
|
||||
|
||||
@@ -139,7 +141,9 @@ function handleDone() {
|
||||
rcloneConfigDialog.value = false
|
||||
aListConfigDialog.value = false
|
||||
customConfigDialog.value = false
|
||||
// 更新存储
|
||||
storage_ref.value.name = customName.value
|
||||
storage_ref.value.type = storageType.value
|
||||
emit('done', storage_ref.value)
|
||||
}
|
||||
|
||||
@@ -155,11 +159,11 @@ function onClose() {
|
||||
<template>
|
||||
<div>
|
||||
<VCard variant="tonal" @click="openStorageDialog">
|
||||
<VDialogCloseBtn v-if="storage.type == 'custom'" @click="onClose" />
|
||||
<VDialogCloseBtn v-if="storageIconDict[storage.type]" @click="onClose" />
|
||||
<VCardText class="flex justify-space-between align-center gap-3">
|
||||
<div class="align-self-start flex-1">
|
||||
<h5 class="text-h6 mb-1">{{ storage.name }}</h5>
|
||||
<template v-if="storage.type != 'custom'">
|
||||
<template v-if="storageIconDict[storage.type]">
|
||||
<div class="mb-3 text-sm" v-if="total">{{ formatBytes(used, 1) }} / {{ formatBytes(total, 1) }}</div>
|
||||
<div v-else-if="isNullOrEmptyObject(storage.config)">{{ t('storage.notConfigured') }}</div>
|
||||
</template>
|
||||
@@ -210,6 +214,15 @@ function onClose() {
|
||||
<VDivider />
|
||||
<VCardText>
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="storageType"
|
||||
:label="t('storage.type')"
|
||||
:hint="t('storage.customTypeHint')"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="customName" :label="t('storage.name')" persistent-hint active />
|
||||
</VCol>
|
||||
|
||||
Reference in New Issue
Block a user