mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-20 07:40:12 +08:00
Merge pull request #183 from Aqr-K/dev
This commit is contained in:
@@ -1,15 +1,25 @@
|
||||
<script lang="ts" setup>
|
||||
import { CustomRule } from '@/api/types'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import filter_svg from '@images/svg/filter.svg'
|
||||
|
||||
// 输入参数
|
||||
const props = defineProps({
|
||||
// 单条规则
|
||||
rule: {
|
||||
type: Object as PropType<CustomRule>,
|
||||
required: true,
|
||||
},
|
||||
// 所有规则
|
||||
rules: {
|
||||
type: Array as PropType<CustomRule[]>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
// 提示框
|
||||
const $toast = useToast()
|
||||
|
||||
// 定义触发的自定义事件
|
||||
const emit = defineEmits(['close', 'change'])
|
||||
|
||||
@@ -43,6 +53,30 @@ function openRuleInfoDialog() {
|
||||
|
||||
// 保存详情数据
|
||||
function saveRuleInfo() {
|
||||
// 有空值
|
||||
if (!ruleId.value && !ruleName.value) {
|
||||
if (!ruleId.value && ruleName.value) {
|
||||
$toast.error('规则ID不能为空')
|
||||
}
|
||||
if (ruleId.value && !ruleName.value) {
|
||||
$toast.error('规则名称不能为空')
|
||||
}
|
||||
if (!ruleId.value && !ruleName.value){
|
||||
$toast.error('规则ID和规则名称不能为空')
|
||||
}
|
||||
return
|
||||
}
|
||||
// ID已存在
|
||||
if (ruleId.value !== props.rule.id && props.rules.find((rule) => rule.id === ruleId.value)) {
|
||||
$toast.error(`规则ID【${ruleId.value}】已存在,请替换`)
|
||||
return
|
||||
}
|
||||
// 规则名称已存在
|
||||
if (ruleName.value !== props.rule.name && props.rules.find((rule) => rule.name === ruleName.value)) {
|
||||
$toast.error(`规则名称【${ruleName.value}】已存在,请替换`)
|
||||
return
|
||||
}
|
||||
// 保存数据
|
||||
ruleInfoDialog.value = false
|
||||
ruleInfo.value.id = ruleId.value
|
||||
ruleInfo.value.name = ruleName.value
|
||||
@@ -78,18 +112,20 @@ function onClose() {
|
||||
<VTextField
|
||||
v-model="ruleId"
|
||||
label="规则ID"
|
||||
placeholder="规则编码"
|
||||
placeholder="必填;不可与其他规则ID重名"
|
||||
hint="字符与数字组合,不能含空格"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="ruleName"
|
||||
label="规则名称"
|
||||
placeholder="别名"
|
||||
placeholder="必填;不可与其他规则名称重名"
|
||||
hint="使用别名便于区分规则"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
@@ -99,6 +135,7 @@ function onClose() {
|
||||
label="包含"
|
||||
hint="必须包含的关键字或正则表达式,多个值使用|分隔"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
@@ -108,6 +145,7 @@ function onClose() {
|
||||
label="排除"
|
||||
hint="不能包含的关键字或正则表达式,多个值使用|分隔"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="6">
|
||||
@@ -117,6 +155,7 @@ function onClose() {
|
||||
label="资源体积(MB)"
|
||||
hint="最小资源文件体积或文件体积范围"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="6">
|
||||
@@ -126,6 +165,7 @@ function onClose() {
|
||||
label="做种人数"
|
||||
hint="最小做种人数或做种人数范围"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="6">
|
||||
@@ -135,6 +175,7 @@ function onClose() {
|
||||
label="发布时间(分钟)"
|
||||
hint="距离资源发布的最小时间间隔"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
import api from '@/api'
|
||||
import { formatFileSize } from '@/@core/utils/formatters'
|
||||
import { DownloaderConf } from '@/api/types'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import type { DownloaderInfo } from '@/api/types'
|
||||
import qbittorrent_image from '@images/logos/qbittorrent.png'
|
||||
import transmission_image from '@images/logos/transmission.png'
|
||||
|
||||
// 定义输入
|
||||
const props = defineProps({
|
||||
// 单个下载器
|
||||
downloader: {
|
||||
type: Object as PropType<DownloaderConf>,
|
||||
required: true,
|
||||
@@ -17,11 +19,19 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 所有下载器
|
||||
downloaders: {
|
||||
type: Array as PropType<DownloaderConf[]>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义触发的自定义事件
|
||||
const emit = defineEmits(['close', 'done', 'change'])
|
||||
|
||||
// 提示框
|
||||
const $toast = useToast()
|
||||
|
||||
// timeout定时器
|
||||
let timeoutTimer: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
@@ -81,6 +91,17 @@ function openDownloaderInfoDialog() {
|
||||
|
||||
// 保存详情数据
|
||||
function saveDownloaderInfo() {
|
||||
// 为空不保存,跳出警告框
|
||||
if (!downloaderName.value) {
|
||||
$toast.error('名称不能为空,请输入后再确定')
|
||||
return
|
||||
}
|
||||
// 重名判断
|
||||
if (props.downloaders.some(item => item.name === downloaderName.value && item!== props.downloader)) {
|
||||
$toast.error(`【${downloaderName.value}】已存在,请替换为其他名称`)
|
||||
return
|
||||
}
|
||||
// 执行保存
|
||||
downloaderInfoDialog.value = false
|
||||
downloaderInfo.value.name = downloaderName.value
|
||||
emit('change', downloaderInfo.value)
|
||||
@@ -162,9 +183,10 @@ onUnmounted(() => {
|
||||
<VTextField
|
||||
v-model="downloaderName"
|
||||
label="名称"
|
||||
placeholder="别名"
|
||||
placeholder="必填;不可与其他名称重名"
|
||||
hint="下载器的别名"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -174,6 +196,7 @@ onUnmounted(() => {
|
||||
placeholder="http(s)://ip:port"
|
||||
hint="服务端地址,格式:http(s)://ip:port"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -183,6 +206,7 @@ onUnmounted(() => {
|
||||
placeholder="admin"
|
||||
hint="登录使用的用户名"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -192,6 +216,7 @@ onUnmounted(() => {
|
||||
label="密码"
|
||||
hint="登录使用的密码"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -200,6 +225,7 @@ onUnmounted(() => {
|
||||
label="自动分类管理"
|
||||
hint="由下载器自动管理分类和下载目录"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -208,6 +234,7 @@ onUnmounted(() => {
|
||||
label="顺序下载"
|
||||
hint="按顺序依次下载文件"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -216,6 +243,7 @@ onUnmounted(() => {
|
||||
label="强制继续"
|
||||
hint="强制继续、强制上传模式"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
@@ -224,9 +252,10 @@ onUnmounted(() => {
|
||||
<VTextField
|
||||
v-model="downloaderName"
|
||||
label="名称"
|
||||
placeholder="别名"
|
||||
placeholder="必填;不可与其他名称重名"
|
||||
hint="下载器的别名"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -236,6 +265,7 @@ onUnmounted(() => {
|
||||
placeholder="http(s)://ip:port"
|
||||
hint="服务端地址,格式:http(s)://ip:port"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -245,6 +275,7 @@ onUnmounted(() => {
|
||||
placeholder="admin"
|
||||
hint="登录使用的用户名"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -254,6 +285,7 @@ onUnmounted(() => {
|
||||
label="密码"
|
||||
hint="登录使用的密码"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
@@ -9,14 +9,22 @@ import filter_group_svg from '@images/svg/filter-group.svg'
|
||||
|
||||
// 输入参数
|
||||
const props = defineProps({
|
||||
// 单个规则组
|
||||
group: {
|
||||
type: Object as PropType<FilterRuleGroup>,
|
||||
required: true,
|
||||
},
|
||||
// 所有规则组
|
||||
groups: {
|
||||
type: Array as PropType<FilterRuleGroup[]>,
|
||||
required: true,
|
||||
},
|
||||
// 媒体类型字典
|
||||
categories: {
|
||||
type: Object as PropType<{ [key: string]: any }>,
|
||||
required: true,
|
||||
},
|
||||
// 自定义规则列表
|
||||
custom_rules: Array as PropType<CustomRule[]>,
|
||||
})
|
||||
|
||||
@@ -171,6 +179,17 @@ function opengroupInfoDialog() {
|
||||
|
||||
// 保存详情数据
|
||||
function savegroupInfo() {
|
||||
// 为空
|
||||
if (!groupName.value) {
|
||||
$toast.error('规则组名称不能为空')
|
||||
return
|
||||
}
|
||||
// 重名判断
|
||||
if (props.groups.some(item => item.name === groupName.value && item !== props.group)) {
|
||||
$toast.error(`规则组名称【${groupName.value}】已存在,请替换`)
|
||||
return
|
||||
}
|
||||
// 保存
|
||||
groupInfoDialog.value = false
|
||||
groupInfo.value.name = groupName.value
|
||||
// 更新到 groupInfo的rule_string
|
||||
@@ -209,13 +228,34 @@ function onClose() {
|
||||
<VCardText>
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="groupName" label="规则组名称" />
|
||||
<VTextField
|
||||
v-model="groupName"
|
||||
label="规则组名称"
|
||||
placeholder="必填;不可与其他规则组重名"
|
||||
hint="自定义规则组名称"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="6" md="3">
|
||||
<VSelect v-model="groupInfo.media_type" label="适用媒体类型" :items="mediaTypeItems" />
|
||||
<VSelect
|
||||
v-model="groupInfo.media_type"
|
||||
label="适用媒体类型"
|
||||
:items="mediaTypeItems"
|
||||
hint="选择规则组适用的媒体类型"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="6" md="3">
|
||||
<VSelect v-model="groupInfo.category" :items="getCategories" label="适用媒体类别" />
|
||||
<VSelect
|
||||
v-model="groupInfo.category"
|
||||
:items="getCategories"
|
||||
label="适用媒体类别"
|
||||
hint="选择规则组适用的媒体类别"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VCardText>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { MediaServerConf, MediaServerLibrary, MediaStatistic } from '@/api/types'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import emby_image from '@images/logos/emby.png'
|
||||
import jellyfin_image from '@images/logos/jellyfin.png'
|
||||
import plex_image from '@images/logos/plex.png'
|
||||
@@ -7,12 +8,21 @@ import api from '@/api'
|
||||
|
||||
// 定义输入
|
||||
const props = defineProps({
|
||||
// 单个媒体服务器
|
||||
mediaserver: {
|
||||
type: Object as PropType<MediaServerConf>,
|
||||
required: true,
|
||||
},
|
||||
// 所有媒体服务器
|
||||
mediaservers: {
|
||||
type: Array as PropType<MediaServerConf[]>,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
// 提示框
|
||||
const $toast = useToast()
|
||||
|
||||
// 定义触发的自定义事件
|
||||
const emit = defineEmits(['close', 'done', 'change'])
|
||||
|
||||
@@ -62,6 +72,17 @@ function openMediaServerInfoDialog() {
|
||||
|
||||
// 保存详情数据
|
||||
function saveMediaServerInfo() {
|
||||
// 为空不保存,跳出警告框
|
||||
if (!mediaServerName.value) {
|
||||
$toast.error('名称不能为空,请输入后再确定')
|
||||
return
|
||||
}
|
||||
// 重名判断
|
||||
if (props.mediaservers.some(item => item.name === mediaServerName.value && item!== props.mediaserver)) {
|
||||
$toast.error(`【${mediaServerName.value}】已存在,请替换为其他名称`)
|
||||
return
|
||||
}
|
||||
// 执行保存
|
||||
mediaServerInfoDialog.value = false
|
||||
mediaServerInfo.value.name = mediaServerName.value
|
||||
emit('change', mediaServerInfo.value)
|
||||
@@ -169,9 +190,10 @@ onMounted(() => {
|
||||
<VTextField
|
||||
v-model="mediaServerName"
|
||||
label="名称"
|
||||
placeholder="别名"
|
||||
placeholder="必填;不可与其他名称重名"
|
||||
hint="媒体服务器的别名"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -181,6 +203,7 @@ onMounted(() => {
|
||||
placeholder="http(s)://ip:port"
|
||||
hint="服务端地址,格式:http(s)://ip:port"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -190,6 +213,7 @@ onMounted(() => {
|
||||
placeholder="http(s)://domain:port"
|
||||
hint="跳转播放页面使用的地址,格式:http(s)://domain:port"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -198,6 +222,7 @@ onMounted(() => {
|
||||
label="API密钥"
|
||||
hint="Emby设置->高级->API密钥中生成的密钥"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
@@ -206,9 +231,10 @@ onMounted(() => {
|
||||
<VTextField
|
||||
v-model="mediaServerName"
|
||||
label="名称"
|
||||
placeholder="别名"
|
||||
placeholder="必填;不可与其他名称重名"
|
||||
hint="媒体服务器的别名"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -218,6 +244,7 @@ onMounted(() => {
|
||||
placeholder="http(s)://ip:port"
|
||||
hint="服务端地址,格式:http(s)://ip:port"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -227,6 +254,7 @@ onMounted(() => {
|
||||
placeholder="http(s)://domain:port"
|
||||
hint="跳转播放页面使用的地址,格式:http(s)://domain:port"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -235,6 +263,7 @@ onMounted(() => {
|
||||
label="API密钥"
|
||||
hint="Jellyfin设置->高级->API密钥中生成的密钥"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
@@ -243,9 +272,10 @@ onMounted(() => {
|
||||
<VTextField
|
||||
v-model="mediaServerName"
|
||||
label="名称"
|
||||
placeholder="别名"
|
||||
placeholder="必填;不可与其他名称重名"
|
||||
hint="媒体服务器的别名"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -255,6 +285,7 @@ onMounted(() => {
|
||||
placeholder="http(s)://ip:port"
|
||||
hint="服务端地址,格式:http(s)://ip:port"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -264,6 +295,7 @@ onMounted(() => {
|
||||
placeholder="http(s)://domain:port"
|
||||
hint="跳转播放页面使用的地址,格式:http(s)://domain:port"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -272,6 +304,7 @@ onMounted(() => {
|
||||
label="X-Plex-Token"
|
||||
hint="浏览器F12->网络,从Plex请求URL中获取的X-Plex-Token"
|
||||
persistent-hint
|
||||
active
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
@@ -285,6 +318,7 @@ onMounted(() => {
|
||||
multiple
|
||||
hint="只有选中的媒体库才会被同步"
|
||||
persistent-hint
|
||||
active
|
||||
append-inner-icon="mdi-refresh"
|
||||
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
||||
/>
|
||||
|
||||
@@ -177,7 +177,11 @@ onMounted(() => {
|
||||
:component-data="{ 'class': 'grid gap-3 grid-customrule-card' }"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<CustomerRuleCard :rule="element" @close="removeCustomRule(element)" @change="onRuleChange" />
|
||||
<CustomerRuleCard
|
||||
:rule="element"
|
||||
:rules="customRules"
|
||||
@close="removeCustomRule(element)"
|
||||
@change="onRuleChange" />
|
||||
</template>
|
||||
</draggable>
|
||||
</VCardText>
|
||||
@@ -206,6 +210,7 @@ onMounted(() => {
|
||||
<template #item="{ element }">
|
||||
<FilterRuleGroupCard
|
||||
:group="element"
|
||||
:groups="filterRuleGroups"
|
||||
:custom_rules="customRules"
|
||||
:categories="mediaCategories"
|
||||
@close="removeFilterRuleGroup(element)"
|
||||
|
||||
@@ -220,6 +220,7 @@ onDeactivated(() => {
|
||||
<template #item="{ element }">
|
||||
<DownloaderCard
|
||||
:downloader="element"
|
||||
:downloaders="downloaders"
|
||||
@close="removeDownloader(element)"
|
||||
@change="onDownloaderChange"
|
||||
:allow-refresh="isRequest"
|
||||
@@ -268,6 +269,7 @@ onDeactivated(() => {
|
||||
<template #item="{ element }">
|
||||
<MediaServerCard
|
||||
:mediaserver="element"
|
||||
:mediaservers="mediaServers"
|
||||
@close="removeMediaServer(element)"
|
||||
@change="onMediaServerChange"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user