mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-21 00:01:26 +08:00
348 lines
10 KiB
Vue
348 lines
10 KiB
Vue
<!-- eslint-disable sonarjs/no-duplicate-string -->
|
|
<script lang="ts" setup>
|
|
import { useToast } from 'vue-toast-notification'
|
|
import draggable from 'vuedraggable'
|
|
import { VRow } from 'vuetify/lib/components/index.mjs'
|
|
import api from '@/api'
|
|
import { TransferDirectoryConf, StorageConf } from '@/api/types'
|
|
import DirectoryCard from '@/components/cards/DirectoryCard.vue'
|
|
import StorageCard from '@/components/cards/StorageCard.vue'
|
|
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
const { t } = useI18n()
|
|
|
|
// 所有下载目录
|
|
const directories = ref<TransferDirectoryConf[]>([])
|
|
|
|
// 所有存储
|
|
const storages = ref<StorageConf[]>([])
|
|
|
|
// 二级分类策略
|
|
const mediaCategories = ref<{ [key: string]: any }>({})
|
|
|
|
// 提示框
|
|
const $toast = useToast()
|
|
|
|
// 进度框
|
|
const progressDialog = ref(false)
|
|
|
|
// 数据源
|
|
const sourceItems = [
|
|
{ 'title': 'TheMovieDb', 'value': 'themoviedb' },
|
|
{ 'title': '豆瓣', 'value': 'douban' },
|
|
]
|
|
|
|
// 系统设置
|
|
const SystemSettings = ref<any>({
|
|
Basic: {
|
|
SCRAP_SOURCE: 'themoviedb',
|
|
MOVIE_RENAME_FORMAT: null,
|
|
TV_RENAME_FORMAT: null,
|
|
},
|
|
})
|
|
|
|
// 加载系统设置
|
|
async function loadSystemSettings() {
|
|
try {
|
|
const result: { [key: string]: any } = await api.get('system/env')
|
|
if (result.success) {
|
|
// 将API返回的值赋值给SystemSettings
|
|
for (const sectionKey of Object.keys(SystemSettings.value) as Array<keyof typeof SystemSettings.value>) {
|
|
Object.keys(SystemSettings.value[sectionKey]).forEach((key: string) => {
|
|
if (result.data.hasOwnProperty(key)) (SystemSettings.value[sectionKey] as any)[key] = result.data[key]
|
|
})
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
}
|
|
|
|
// 重载系统生效配置
|
|
async function reloadSystem() {
|
|
progressDialog.value = true
|
|
try {
|
|
const result: { [key: string]: any } = await api.get('system/reload')
|
|
if (result.success) $toast.success(t('setting.system.reloadSuccess'))
|
|
else $toast.error(t('setting.system.reloadFailed'))
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
progressDialog.value = false
|
|
}
|
|
|
|
// 移动结束
|
|
function orderDirectoryCards() {
|
|
// 更新所有目录的优先级
|
|
directories.value.forEach((item, index) => {
|
|
item.priority = index
|
|
})
|
|
}
|
|
|
|
// 查询存储
|
|
async function loadStorages() {
|
|
try {
|
|
const result: { [key: string]: any } = await api.get('system/setting/Storages')
|
|
|
|
storages.value = result.data?.value ?? []
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
}
|
|
|
|
// 保存存储
|
|
async function saveStorages() {
|
|
try {
|
|
const result: { [key: string]: any } = await api.post('system/setting/Storages', storages.value)
|
|
if (result.success) $toast.success(t('setting.directory.storageSaveSuccess'))
|
|
else $toast.error(t('setting.directory.storageSaveFailed'))
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
}
|
|
|
|
// 查询目录
|
|
async function loadDirectories() {
|
|
try {
|
|
const result: { [key: string]: any } = await api.get('system/setting/Directories')
|
|
directories.value = result.data?.value ?? []
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
}
|
|
|
|
// 保存目录
|
|
async function saveDirectories() {
|
|
orderDirectoryCards()
|
|
try {
|
|
const names = directories.value.map(item => item.name)
|
|
if (new Set(names).size !== names.length) {
|
|
$toast.error(t('setting.directory.duplicateDirectoryName'))
|
|
return
|
|
}
|
|
const result: { [key: string]: any } = await api.post('system/setting/Directories', directories.value)
|
|
if (result.success) {
|
|
$toast.success(t('setting.directory.directorySaveSuccess'))
|
|
await reloadSystem()
|
|
} else $toast.error(t('setting.directory.directorySaveFailed'))
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
}
|
|
|
|
// 添加媒体库目录
|
|
function addDirectory() {
|
|
let name = `${t('setting.directory.defaultDirName')}${directories.value.length + 1}`
|
|
while (directories.value.some(item => item.name === name)) {
|
|
name = `${t('setting.directory.defaultDirName')}${
|
|
parseInt(name.split(t('setting.directory.defaultDirName'))[1]) + 1
|
|
}`
|
|
}
|
|
directories.value.push({
|
|
name: name,
|
|
storage: 'local',
|
|
download_path: '',
|
|
priority: -1,
|
|
monitor_type: '',
|
|
media_type: '',
|
|
media_category: '',
|
|
transfer_type: '',
|
|
})
|
|
orderDirectoryCards()
|
|
}
|
|
|
|
// 移除媒体库目录
|
|
function removeDirectory(directory: TransferDirectoryConf) {
|
|
const index = directories.value.indexOf(directory)
|
|
if (index > -1) {
|
|
directories.value.splice(index, 1)
|
|
}
|
|
}
|
|
|
|
// 调用API查询自动分类配置
|
|
async function loadMediaCategories() {
|
|
try {
|
|
mediaCategories.value = await api.get('media/category')
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
}
|
|
|
|
// 添加存储
|
|
function addStorage() {
|
|
storages.value.push({
|
|
name: `${t('storage.custom')} ${storages.value.length + 1}`,
|
|
type: 'custom',
|
|
config: {},
|
|
})
|
|
}
|
|
|
|
// 移除存储
|
|
function removeStorage(storage: StorageConf) {
|
|
const index = storages.value.indexOf(storage)
|
|
if (index > -1) {
|
|
storages.value.splice(index, 1)
|
|
}
|
|
}
|
|
|
|
// 更新存储
|
|
async function updatedStorage(storage: StorageConf) {
|
|
const index = storages.value.indexOf(storage)
|
|
if (index > -1) {
|
|
storages.value[index] = storage
|
|
}
|
|
}
|
|
|
|
// 保存设置
|
|
async function saveSystemSettings(value: any) {
|
|
try {
|
|
const result: { [key: string]: any } = await api.post('system/env', value)
|
|
if (result.success) {
|
|
$toast.success(t('setting.directory.organizeSaveSuccess'))
|
|
} else $toast.error(t('setting.directory.organizeSaveFailed'))
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
}
|
|
|
|
// 加载数据
|
|
onMounted(() => {
|
|
loadDirectories()
|
|
loadStorages()
|
|
loadMediaCategories()
|
|
loadSystemSettings()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<VRow>
|
|
<VCol cols="12">
|
|
<VCard>
|
|
<VCardItem>
|
|
<VCardTitle>{{ t('setting.directory.storage') }}</VCardTitle>
|
|
<VCardSubtitle>{{ t('setting.directory.storageDesc') }}</VCardSubtitle>
|
|
</VCardItem>
|
|
<VCardText>
|
|
<draggable
|
|
v-model="storages"
|
|
handle=".cursor-move"
|
|
item-key="name"
|
|
tag="div"
|
|
:component-data="{ 'class': 'grid gap-3 grid-app-card' }"
|
|
>
|
|
<template #item="{ element }">
|
|
<StorageCard :storage="element" @close="removeStorage(element)" @done="updatedStorage" />
|
|
</template>
|
|
</draggable>
|
|
</VCardText>
|
|
<VCardText>
|
|
<VForm @submit.prevent="() => {}">
|
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
|
<VBtn type="submit" class="me-2" @click="saveStorages"> {{ t('common.save') }} </VBtn>
|
|
<VBtn color="success" variant="tonal" @click="addStorage">
|
|
<VIcon icon="mdi-plus" />
|
|
</VBtn>
|
|
</div>
|
|
</VForm>
|
|
</VCardText>
|
|
</VCard>
|
|
</VCol>
|
|
</VRow>
|
|
<VRow>
|
|
<VCol cols="12">
|
|
<VCard>
|
|
<VCardItem>
|
|
<VCardTitle>{{ t('setting.directory.directory') }}</VCardTitle>
|
|
<VCardSubtitle>{{ t('setting.directory.directoryDesc') }}</VCardSubtitle>
|
|
</VCardItem>
|
|
<VCardText>
|
|
<draggable
|
|
v-model="directories"
|
|
handle=".cursor-move"
|
|
item-key="pri"
|
|
tag="div"
|
|
@end="orderDirectoryCards"
|
|
:component-data="{ 'class': 'grid gap-3 grid-directory-card items-start' }"
|
|
>
|
|
<template #item="{ element }">
|
|
<DirectoryCard
|
|
:directory="element"
|
|
:categories="mediaCategories"
|
|
:storages="storages"
|
|
@update:modelValue="(value: any) => {element.download_path = value?.download; element.library_path = value?.library}"
|
|
@close="removeDirectory(element)"
|
|
/>
|
|
</template>
|
|
</draggable>
|
|
</VCardText>
|
|
<VCardText>
|
|
<VForm @submit.prevent="() => {}">
|
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
|
<VBtn type="submit" @click="saveDirectories"> {{ t('common.save') }} </VBtn>
|
|
<VBtn color="success" variant="tonal" @click="addDirectory">
|
|
<VIcon icon="mdi-plus" />
|
|
</VBtn>
|
|
</div>
|
|
</VForm>
|
|
</VCardText>
|
|
</VCard>
|
|
</VCol>
|
|
</VRow>
|
|
<VRow>
|
|
<VCol cols="12">
|
|
<VCard>
|
|
<VCardItem>
|
|
<VCardTitle>{{ t('setting.directory.organizeAndScrap') }}</VCardTitle>
|
|
<VCardSubtitle>{{ t('setting.directory.organizeAndScrapDesc') }}</VCardSubtitle>
|
|
</VCardItem>
|
|
<VCardText>
|
|
<VRow>
|
|
<VCol cols="12" md="6">
|
|
<VSelect
|
|
v-model="SystemSettings.Basic.SCRAP_SOURCE"
|
|
:items="sourceItems"
|
|
:label="t('setting.directory.scrapSource')"
|
|
:hint="t('setting.directory.scrapSourceHint')"
|
|
persistent-hint
|
|
prepend-inner-icon="mdi-database"
|
|
/>
|
|
</VCol>
|
|
<VCol cols="12">
|
|
<VTextarea
|
|
v-model="SystemSettings.Basic.MOVIE_RENAME_FORMAT"
|
|
:label="t('setting.directory.movieRenameFormat')"
|
|
:hint="t('setting.directory.movieRenameFormatHint')"
|
|
persistent-hint
|
|
clearable
|
|
active
|
|
prepend-inner-icon="mdi-movie-open"
|
|
/>
|
|
</VCol>
|
|
<VCol cols="12">
|
|
<VTextarea
|
|
v-model="SystemSettings.Basic.TV_RENAME_FORMAT"
|
|
:label="t('setting.directory.tvRenameFormat')"
|
|
:hint="t('setting.directory.tvRenameFormatHint')"
|
|
persistent-hint
|
|
clearable
|
|
active
|
|
prepend-inner-icon="mdi-television"
|
|
/>
|
|
</VCol>
|
|
</VRow>
|
|
</VCardText>
|
|
<VCardText>
|
|
<VForm @submit.prevent="() => {}">
|
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
|
<VBtn type="submit" @click="saveSystemSettings(SystemSettings.Basic)"> {{ t('common.save') }}</VBtn>
|
|
</div>
|
|
</VForm>
|
|
</VCardText>
|
|
</VCard>
|
|
</VCol>
|
|
</VRow>
|
|
<!-- 进度框 -->
|
|
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="t('setting.system.reloading')" />
|
|
</template>
|