style: Update styles.scss and types.ts to include config property in DownloaderConf and MediaServerConf

This commit is contained in:
jxxghp
2024-08-11 15:09:56 +08:00
parent 235eb82c45
commit 8bd0f7a589
7 changed files with 428 additions and 97 deletions
+2 -2
View File
@@ -858,7 +858,7 @@ export interface DownloaderConf {
// 是否默认 // 是否默认
default: boolean default: boolean
// 配置 // 配置
config?: { [key: string]: any } config: { [key: string]: any }
// 是否启用 // 是否启用
enabled: boolean enabled: boolean
} }
@@ -902,7 +902,7 @@ export interface MediaServerConf {
// 类型 emby/jellyfin/plex // 类型 emby/jellyfin/plex
type: string type: string
// 配置 // 配置
config?: { [key: string]: any } config: { [key: string]: any }
// 是否启用 // 是否启用
enabled: boolean enabled: boolean
// 同步媒体体库列表 // 同步媒体体库列表
+71 -61
View File
@@ -10,7 +10,33 @@ const props = defineProps({
}) })
// 定义触发的自定义事件 // 定义触发的自定义事件
const emit = defineEmits(['close']) const emit = defineEmits(['close', 'change'])
// 规则详情弹窗
const ruleInfoDialog = ref(false)
// 规则详情
const ruleInfo = ref<CustomRule>({
id: '',
name: '',
include: '',
exclude: '',
size_range: '',
seeders: '',
publish_time: '',
})
// 打开详情弹窗
function openRuleInfoDialog() {
ruleInfo.value = props.rule
ruleInfoDialog.value = true
}
// 保存详情数据
function saveRuleInfo() {
ruleInfoDialog.value = false
emit('change', ruleInfo.value)
}
// 按钮点击 // 按钮点击
function onClose() { function onClose() {
@@ -19,64 +45,48 @@ function onClose() {
</script> </script>
<template> <template>
<VCard variant="tonal"> <div>
<DialogCloseBtn @click="onClose" /> <VCard variant="tonal" @click="openRuleInfoDialog">
<VCardItem> <DialogCloseBtn @click="onClose" />
<VTextField <VCardText class="flex justify-space-between align-center gap-3">
v-model="props.rule.id" <div class="align-self-start">
variant="underlined" <h5 class="text-h6 mb-1">{{ props.rule.id }}</h5>
label="规则编码" <div class="text-body-1 mb-3">{{ props.rule.name }}</div>
class="me-20 text-high-emphasis font-weight-bold" </div>
/> </VCardText>
<span class="absolute top-3 right-12"> </VCard>
<IconBtn> <VDialog v-model="ruleInfoDialog" scrollable max-width="40rem">
<VIcon class="cursor-move" icon="mdi-drag" /> <VCard :title="`${props.rule.id} - 配置`" class="rounded-t">
</IconBtn> <DialogCloseBtn v-model="ruleInfoDialog" />
</span> <VDivider />
</VCardItem> <VCardText>
<VCardText> <VForm>
<VForm> <VRow>
<VRow> <VCol cols="12">
<VCol cols="12"> <VTextField v-model="ruleInfo.name" label="规则名称" />
<VTextField v-model="props.rule.name" variant="underlined" label="规则名称" /> </VCol>
</VCol> <VCol cols="12">
<VCol cols="12"> <VTextField v-model="ruleInfo.include" placeholder="关键字/正则表达式" label="包含" />
<VTextField </VCol>
v-model="props.rule.include" <VCol cols="12">
variant="underlined" <VTextField v-model="ruleInfo.exclude" placeholder="关键字/正则表达式" label="排除" />
placeholder="关键字/正则表达式" </VCol>
label="包含" <VCol cols="6">
/> <VTextField v-model="ruleInfo.size_range" placeholder="0/1-10" label="大小范围(MB" />
</VCol> </VCol>
<VCol cols="12"> <VCol cols="6">
<VTextField <VTextField v-model="ruleInfo.seeders" placeholder="0/1-10" label="做种人数" />
v-model="props.rule.exclude" </VCol>
variant="underlined" <VCol cols="6">
placeholder="关键字/正则表达式" <VTextField v-model="ruleInfo.publish_time" placeholder="0" label="发布时间(分钟)" />
label="排除" </VCol>
/> </VRow>
</VCol> </VForm>
<VCol cols="6"> </VCardText>
<VTextField <VCardActions class="pt-3">
v-model="props.rule.size_range" <VBtn @click="saveRuleInfo" variant="elevated" prepend-icon="mdi-content-save" class="px-5"> 确定 </VBtn>
variant="underlined" </VCardActions>
placeholder="0/1-10" </VCard>
label="大小范围(MB" </VDialog>
/> </div>
</VCol>
<VCol cols="6">
<VTextField v-model="props.rule.size_range" variant="underlined" placeholder="0/1-10" label="做种人数" />
</VCol>
<VCol cols="6">
<VTextField
v-model="props.rule.publish_time"
variant="underlined"
placeholder="0"
label="发布时间(分钟)"
/>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</template> </template>
+170 -18
View File
@@ -12,12 +12,39 @@ const props = defineProps({
}, },
}) })
// 定义触发的自定义事件
const emit = defineEmits(['close', 'change'])
// 上传速率 // 上传速率
const upload_rate = ref(0) const upload_rate = ref(0)
// 下载速度 // 下载速度
const download_rate = ref(0) const download_rate = ref(0)
// 下载器详情弹窗
const downloaderInfoDialog = ref(false)
// 下载器详情
const downloaderInfo = ref<DownloaderConf>({
name: '',
type: '',
default: false,
enabled: false,
config: {},
})
// 打开详情弹窗
function openDownloaderInfoDialog() {
downloaderInfo.value = props.downloader
downloaderInfoDialog.value = true
}
// 保存详情数据
function saveDownloaderInfo() {
downloaderInfoDialog.value = false
emit('change', downloaderInfo.value)
}
// 速度 // 速度
const getSpeedText = computed(() => { const getSpeedText = computed(() => {
return `${upload_rate.value}/s ↓ ${download_rate.value}/s` return `${upload_rate.value}/s ↓ ${download_rate.value}/s`
@@ -35,28 +62,153 @@ const getIcon = computed(() => {
} }
}) })
// 定义触发的自定义事件
const emit = defineEmits(['close'])
// 按钮点击 // 按钮点击
function onClose() { function onClose() {
emit('close') emit('close')
} }
</script> </script>
<template> <template>
<VCard variant="tonal"> <div>
<DialogCloseBtn @click="onClose" /> <VCard variant="tonal" @click="openDownloaderInfoDialog">
<span class="absolute top-3 right-12"> <DialogCloseBtn @click="onClose" />
<IconBtn> <span class="absolute top-3 right-12">
<VIcon class="cursor-move" icon="mdi-drag" /> <IconBtn>
</IconBtn> <VIcon class="cursor-move" icon="mdi-drag" />
</span> </IconBtn>
<VCardText class="flex justify-space-between align-center gap-3"> </span>
<div class="align-self-start"> <VCardText class="flex justify-space-between align-center gap-3">
<h5 class="text-h6 mb-1">{{ downloader.name }}</h5> <div class="align-self-start">
<div class="text-body-1 mb-3">{{ getSpeedText }}</div> <div>
</div> <VBadge v-if="props.downloader.default && props.downloader.enabled" dot inline color="success" />
<VImg :src="getIcon" cover class="mt-10" max-width="4rem" /> <span class="text-h6 mb-1 ms-1">{{ downloader.name }}</span>
</VCardText> </div>
</VCard> <div class="text-body-1 mb-3">{{ getSpeedText }}</div>
</div>
<VImg :src="getIcon" cover class="mt-10" max-width="4rem" />
</VCardText>
</VCard>
<VDialog v-model="downloaderInfoDialog" scrollable max-width="40rem">
<VCard :title="`${props.downloader.name} - 配置`" class="rounded-t">
<DialogCloseBtn v-model="downloaderInfoDialog" />
<VDivider />
<VCardText>
<VForm>
<VRow>
<VCol cols="12" md="6">
<VSwitch v-model="downloaderInfo.enabled" label="启用下载器" />
</VCol>
<VCol cols="12" md="6">
<VSwitch v-model="downloaderInfo.default" label="默认下载器" :disabled="!downloaderInfo.enabled" />
</VCol>
</VRow>
<VRow v-if="downloaderInfo.type == 'qbittorrent'">
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.name"
label="名称"
placeholder="别名"
hint="下载器的别名"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.config.QB_HOST"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址,格式:http(s)://ip:port"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.config.QB_USER"
label="用户名"
placeholder="admin"
hint="登录使用的用户名"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.config.QB_PASSWORD"
type="password"
label="密码"
hint="登录使用的密码"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VSwitch
v-model="downloaderInfo.config.QB_CATEGORY"
label="自动分类管理"
hint="由下载器自动管理分类和下载目录"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VSwitch
v-model="downloaderInfo.config.QB_SEQUENTIAL"
label="顺序下载"
hint="按顺序依次下载文件"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VSwitch
v-model="downloaderInfo.config.QB_FORCE_RESUME"
label="强制继续"
hint="强制继续、强制上传模式"
persistent-hint
/>
</VCol>
</VRow>
<VRow v-if="downloaderInfo.type == 'transmission'">
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.name"
label="名称"
placeholder="别名"
hint="下载器的别名"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.config.TR_HOST"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址,格式:http(s)://ip:port"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.config.TR_USER"
label="用户名"
placeholder="admin"
hint="登录使用的用户名"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.config.TR_PASSWORD"
type="password"
label="密码"
hint="登录使用的密码"
persistent-hint
/>
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardActions class="pt-3">
<VBtn @click="saveDownloaderInfo" variant="elevated" prepend-icon="mdi-content-save" class="px-5">
确定
</VBtn>
</VCardActions>
</VCard>
</VDialog>
</div>
</template> </template>
+169 -13
View File
@@ -12,6 +12,32 @@ const props = defineProps({
}, },
}) })
// 定义触发的自定义事件
const emit = defineEmits(['close', 'change'])
// 媒体服务器详情弹窗
const mediaServerInfoDialog = ref(false)
// 媒体服务器详情
const mediaServerInfo = ref<MediaServerConf>({
name: '',
type: '',
enabled: false,
config: {},
})
// 打开详情弹窗
function openMediaServerInfoDialog() {
mediaServerInfo.value = props.mediaserver
mediaServerInfoDialog.value = true
}
// 保存详情数据
function saveMediaServerInfo() {
mediaServerInfoDialog.value = false
emit('change', mediaServerInfo.value)
}
// 根据存储类型选择图标 // 根据存储类型选择图标
const getIcon = computed(() => { const getIcon = computed(() => {
switch (props.mediaserver.type) { switch (props.mediaserver.type) {
@@ -24,23 +50,153 @@ const getIcon = computed(() => {
} }
}) })
// 定义触发的自定义事件
const emit = defineEmits(['close'])
// 按钮点击 // 按钮点击
function onClose() { function onClose() {
emit('close') emit('close')
} }
</script> </script>
<template> <template>
<VCard variant="tonal"> <div>
<DialogCloseBtn @click="onClose" /> <VCard variant="tonal" @click="openMediaServerInfoDialog">
<VCardText class="flex justify-space-between align-center gap-3"> <DialogCloseBtn @click="onClose" />
<div class="align-self-start"> <VCardText class="flex justify-space-between align-center gap-3">
<h5 class="text-h6 mb-1">{{ mediaserver.name }}</h5> <div class="align-self-start">
<div class="text-body-1 mb-3"></div> <div class="text-h6 mb-1">{{ mediaserver.name }}</div>
</div> <div class="text-body-1 mb-3"></div>
<VImg :src="getIcon" cover class="mt-5 me-7" max-width="4rem" /> </div>
</VCardText> <VImg :src="getIcon" cover class="mt-5 me-7" max-width="4rem" />
</VCard> </VCardText>
</VCard>
<VDialog v-model="mediaServerInfoDialog" scrollable max-width="40rem">
<VCard :title="`${props.mediaserver.name} - 配置`" class="rounded-t">
<DialogCloseBtn v-model="mediaServerInfoDialog" />
<VDivider />
<VCardText>
<VForm>
<VRow>
<VCol cols="12" md="6">
<VSwitch v-model="mediaServerInfo.enabled" label="启用媒体服务器" />
</VCol>
</VRow>
<VRow v-if="mediaServerInfo.type == 'emby'">
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.name"
label="名称"
placeholder="别名"
hint="媒体服务器的别名"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.EMBY_HOST"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址,格式:http(s)://ip:port"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.EMBY_PLAY_HOST"
label="外网播放地址"
placeholder="http(s)://domain:port"
hint="跳转播放页面使用的地址,格式:http(s)://domain:port"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.EMBY_API_KEY"
label="API密钥"
hint="Emby设置->高级->API密钥中生成的密钥"
persistent-hint
/>
</VCol>
</VRow>
<VRow v-if="mediaServerInfo.type == 'jellyfin'">
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.name"
label="名称"
placeholder="别名"
hint="媒体服务器的别名"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.JELLYFIN_HOST"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址,格式:http(s)://ip:port"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.JELLYFIN_PLAY_HOST"
label="外网播放地址"
placeholder="http(s)://domain:port"
hint="跳转播放页面使用的地址,格式:http(s)://domain:port"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.JELLYFIN_API_KEY"
label="API密钥"
hint="Jellyfin设置->高级->API密钥中生成的密钥"
persistent-hint
/>
</VCol>
</VRow>
<VRow v-if="mediaServerInfo.type == 'plex'">
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.name"
label="名称"
placeholder="别名"
hint="媒体服务器的别名"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.PLEX_HOST"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址,格式:http(s)://ip:port"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.PLEX_PLAY_HOST"
label="外网播放地址"
placeholder="http(s)://domain:port"
hint="跳转播放页面使用的地址,格式:http(s)://domain:port"
persistent-hint
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.PLEX_TOKEN"
label="X-Plex-Token"
hint="浏览器F12->网络,从Plex请求URL中获取的X-Plex-Token"
persistent-hint
/>
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardActions class="pt-3">
<VBtn @click="saveMediaServerInfo" variant="elevated" prepend-icon="mdi-content-save" class="px-5">
确定
</VBtn>
</VCardActions>
</VCard>
</VDialog>
</div>
</template> </template>
+5
View File
@@ -176,6 +176,11 @@
padding-block-end: 1rem; padding-block-end: 1rem;
} }
.grid-customrule-card {
grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
padding-block-end: 1rem;
}
.grid-subscribe-card { .grid-subscribe-card {
grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
padding-block-end: 1rem; padding-block-end: 1rem;
+8 -2
View File
@@ -93,6 +93,12 @@ function addFilterRuleGroup() {
}) })
} }
// 规则变化时赋值
function onRuleChange(rule: CustomRule) {
const index = customRules.value.findIndex(item => item.id === rule.id)
if (index !== -1) customRules.value[index] = rule
}
// 移除规则组 // 移除规则组
function removeFilterRuleGroup(rule: FilterRuleGroup) { function removeFilterRuleGroup(rule: FilterRuleGroup) {
const index = filterRuleGroups.value.findIndex(item => item.name === rule.name) const index = filterRuleGroups.value.findIndex(item => item.name === rule.name)
@@ -148,10 +154,10 @@ onMounted(() => {
handle=".cursor-move" handle=".cursor-move"
item-key="name" item-key="name"
tag="div" tag="div"
:component-data="{ 'class': 'grid gap-3 grid-filterrule-card' }" :component-data="{ 'class': 'grid gap-3 grid-customrule-card' }"
> >
<template #item="{ element }"> <template #item="{ element }">
<CustomerRuleCard :rule="element" @close="removeCustomRule(element)" /> <CustomerRuleCard :rule="element" @close="removeCustomRule(element)" @change="onRuleChange" />
</template> </template>
</draggable> </draggable>
</VCardText> </VCardText>
+3 -1
View File
@@ -98,6 +98,7 @@ function addDownloader(downloader: string) {
type: downloader, type: downloader,
default: false, default: false,
enabled: false, enabled: false,
config: {},
}) })
} }
@@ -113,6 +114,7 @@ function addMediaServer(mediaserver: string) {
name: `服务器${mediaServers.value.length + 1}`, name: `服务器${mediaServers.value.length + 1}`,
type: mediaserver, type: mediaserver,
enabled: false, enabled: false,
config: {},
}) })
} }
@@ -188,7 +190,7 @@ onMounted(() => {
<VIcon icon="mdi-plus" /> <VIcon icon="mdi-plus" />
<VMenu activator="parent" close-on-content-click> <VMenu activator="parent" close-on-content-click>
<VList> <VList>
<VListItem variant="plain" @click="addDownloader('qbittottent')"> <VListItem variant="plain" @click="addDownloader('qbittorrent')">
<VListItemTitle>Qbittorrent</VListItemTitle> <VListItemTitle>Qbittorrent</VListItemTitle>
</VListItem> </VListItem>
<VListItem variant="plain" @click="addDownloader('transmission')"> <VListItem variant="plain" @click="addDownloader('transmission')">