mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-10 17:42:50 +08:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31047b0d44 | ||
|
|
7c2b724d10 | ||
|
|
ca5670f06b | ||
|
|
427e05871d | ||
|
|
bef56bdb56 | ||
|
|
d450d02e18 | ||
|
|
85a766cc7b | ||
|
|
a473f356c9 | ||
|
|
52b5fdf383 | ||
|
|
b886f02043 | ||
|
|
61963ea497 | ||
|
|
2f9b27ad9e | ||
|
|
9334109767 | ||
|
|
2bc52576d9 | ||
|
|
700d2c4a51 | ||
|
|
103bdb32c8 | ||
|
|
92b745e180 | ||
|
|
a2007083b8 | ||
|
|
36a5f7ff29 | ||
|
|
f727aea51d | ||
|
|
936ca24328 | ||
|
|
62f49b6087 | ||
|
|
e9ddbf9962 | ||
|
|
196cf522e6 | ||
|
|
3fce3bf4a7 | ||
|
|
1cfee25695 | ||
|
|
5711285a77 | ||
|
|
e6f537ca3a | ||
|
|
3b5220af57 | ||
|
|
fa6b4b1d2d | ||
|
|
7968e5374b | ||
|
|
64997ebe45 | ||
|
|
f8592b01e2 | ||
|
|
087474f514 | ||
|
|
1725088f05 | ||
|
|
ec1b756a3d | ||
|
|
76a06e0817 | ||
|
|
02fb608d7b | ||
|
|
e17fc2fc12 | ||
|
|
4f6c317652 | ||
|
|
46c198be26 | ||
|
|
8552203d43 | ||
|
|
139eaa7016 | ||
|
|
d81120ab8f | ||
|
|
6353d56beb | ||
|
|
aa05496b42 | ||
|
|
dc15e537d8 |
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -110,4 +110,4 @@
|
|||||||
"i18n-ally.localesPaths": [
|
"i18n-ally.localesPaths": [
|
||||||
"src/locales"
|
"src/locales"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "moviepilot",
|
"name": "moviepilot",
|
||||||
"version": "2.5.0",
|
"version": "2.5.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": "dist/service.js",
|
"bin": "dist/service.js",
|
||||||
|
|||||||
@@ -1305,3 +1305,49 @@ export interface Workflow {
|
|||||||
// 最后执行时间
|
// 最后执行时间
|
||||||
last_time?: string
|
last_time?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 种子缓存项
|
||||||
|
export interface TorrentCacheItem {
|
||||||
|
// 种子hash(用于操作标识)
|
||||||
|
hash: string
|
||||||
|
// 站点域名
|
||||||
|
domain: string
|
||||||
|
// 种子标题
|
||||||
|
title: string
|
||||||
|
// 种子描述
|
||||||
|
description?: string
|
||||||
|
// 种子大小
|
||||||
|
size: number
|
||||||
|
// 发布时间
|
||||||
|
pubdate?: string
|
||||||
|
// 站点名称
|
||||||
|
site_name?: string
|
||||||
|
// 识别的媒体名称
|
||||||
|
media_name?: string
|
||||||
|
// 识别的媒体年份
|
||||||
|
media_year?: string
|
||||||
|
// 识别的媒体类型
|
||||||
|
media_type?: string
|
||||||
|
// 季集信息
|
||||||
|
season_episode?: string
|
||||||
|
// 资源信息
|
||||||
|
resource_term?: string
|
||||||
|
// 种子链接
|
||||||
|
enclosure?: string
|
||||||
|
// 详情页面
|
||||||
|
page_url?: string
|
||||||
|
// 海报图片
|
||||||
|
poster_path?: string
|
||||||
|
// 背景图片
|
||||||
|
backdrop_path?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 种子缓存数据
|
||||||
|
export interface TorrentCacheData {
|
||||||
|
// 缓存数量
|
||||||
|
count: number
|
||||||
|
// 站点数量
|
||||||
|
sites: number
|
||||||
|
// 缓存数据
|
||||||
|
data: TorrentCacheItem[]
|
||||||
|
}
|
||||||
|
|||||||
@@ -117,7 +117,13 @@ function onClose() {
|
|||||||
max-width="40rem"
|
max-width="40rem"
|
||||||
:fullscreen="!display.mdAndUp.value"
|
:fullscreen="!display.mdAndUp.value"
|
||||||
>
|
>
|
||||||
<VCard :title="t('customRule.title', { id: props.rule.id })">
|
<VCard>
|
||||||
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-filter-outline" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ t('customRule.title', { id: props.rule.id }) }}</VCardTitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn v-model="ruleInfoDialog" />
|
<VDialogCloseBtn v-model="ruleInfoDialog" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
@@ -131,6 +137,7 @@ function onClose() {
|
|||||||
:hint="t('customRule.hint.ruleId')"
|
:hint="t('customRule.hint.ruleId')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-identifier"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -141,6 +148,7 @@ function onClose() {
|
|||||||
:hint="t('customRule.hint.ruleName')"
|
:hint="t('customRule.hint.ruleName')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -151,6 +159,7 @@ function onClose() {
|
|||||||
:hint="t('customRule.hint.include')"
|
:hint="t('customRule.hint.include')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-plus-circle"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -161,6 +170,7 @@ function onClose() {
|
|||||||
:hint="t('customRule.hint.exclude')"
|
:hint="t('customRule.hint.exclude')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-minus-circle"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6">
|
<VCol cols="6">
|
||||||
@@ -171,6 +181,7 @@ function onClose() {
|
|||||||
:hint="t('customRule.hint.sizeRange')"
|
:hint="t('customRule.hint.sizeRange')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-harddisk"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6">
|
<VCol cols="6">
|
||||||
@@ -181,6 +192,7 @@ function onClose() {
|
|||||||
:hint="t('customRule.hint.seeders')"
|
:hint="t('customRule.hint.seeders')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-account-group"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6">
|
<VCol cols="6">
|
||||||
@@ -191,6 +203,7 @@ function onClose() {
|
|||||||
:hint="t('customRule.hint.publishTime')"
|
:hint="t('customRule.hint.publishTime')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-calendar-clock"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -200,7 +200,14 @@ onUnmounted(() => {
|
|||||||
max-width="40rem"
|
max-width="40rem"
|
||||||
:fullscreen="!display.mdAndUp.value"
|
:fullscreen="!display.mdAndUp.value"
|
||||||
>
|
>
|
||||||
<VCard :title="`${props.downloader.name} - ${t('downloader.title')}`">
|
<VCard>
|
||||||
|
<VCardItem class="py-2">
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-download" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ t('common.config') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ props.downloader.name }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn v-model="downloaderInfoDialog" />
|
<VDialogCloseBtn v-model="downloaderInfoDialog" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
@@ -226,6 +233,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.name')"
|
:hint="t('downloader.name')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -236,6 +244,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.host')"
|
:hint="t('downloader.host')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -245,6 +254,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.username')"
|
:hint="t('downloader.username')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-account"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -255,6 +265,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.password')"
|
:hint="t('downloader.password')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -303,6 +314,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.name')"
|
:hint="t('downloader.name')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -313,6 +325,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.host')"
|
:hint="t('downloader.host')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -322,6 +335,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.username')"
|
:hint="t('downloader.username')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-account"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -332,6 +346,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.password')"
|
:hint="t('downloader.password')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -343,6 +358,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.customTypeHint')"
|
:hint="t('downloader.customTypeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-cog"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -352,6 +368,7 @@ onUnmounted(() => {
|
|||||||
:hint="t('downloader.nameRequired')"
|
:hint="t('downloader.nameRequired')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ function onClose() {
|
|||||||
:hint="t('filterRule.groupName')"
|
:hint="t('filterRule.groupName')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6" md="3">
|
<VCol cols="6" md="3">
|
||||||
@@ -253,6 +254,7 @@ function onClose() {
|
|||||||
:hint="t('filterRule.mediaType')"
|
:hint="t('filterRule.mediaType')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-movie-open"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6" md="3">
|
<VCol cols="6" md="3">
|
||||||
@@ -263,6 +265,7 @@ function onClose() {
|
|||||||
:hint="t('filterRule.category')"
|
:hint="t('filterRule.category')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-folder-open"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -211,7 +211,14 @@ onMounted(() => {
|
|||||||
max-width="40rem"
|
max-width="40rem"
|
||||||
:fullscreen="!display.mdAndUp.value"
|
:fullscreen="!display.mdAndUp.value"
|
||||||
>
|
>
|
||||||
<VCard :title="`${props.mediaserver.name} - ${t('common.config')}`">
|
<VCard>
|
||||||
|
<VCardItem class="py-2">
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-cog" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ t('common.config') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ props.mediaserver.name }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn v-model="mediaServerInfoDialog" />
|
<VDialogCloseBtn v-model="mediaServerInfoDialog" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
@@ -230,6 +237,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.serverAlias')"
|
:hint="t('mediaserver.serverAlias')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -240,6 +248,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.hostHint')"
|
:hint="t('mediaserver.hostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -250,6 +259,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.playHostHint')"
|
:hint="t('mediaserver.playHostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-play-network"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -259,6 +269,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.embyApiKeyHint')"
|
:hint="t('mediaserver.embyApiKeyHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -273,6 +284,7 @@ onMounted(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
append-inner-icon="mdi-refresh"
|
append-inner-icon="mdi-refresh"
|
||||||
|
prepend-inner-icon="mdi-library"
|
||||||
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -286,6 +298,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.serverAlias')"
|
:hint="t('mediaserver.serverAlias')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -296,6 +309,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.hostHint')"
|
:hint="t('mediaserver.hostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -306,6 +320,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.playHostHint')"
|
:hint="t('mediaserver.playHostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-play-network"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -315,6 +330,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.jellyfinApiKeyHint')"
|
:hint="t('mediaserver.jellyfinApiKeyHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -329,6 +345,7 @@ onMounted(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
append-inner-icon="mdi-refresh"
|
append-inner-icon="mdi-refresh"
|
||||||
|
prepend-inner-icon="mdi-library"
|
||||||
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -342,6 +359,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.serverAlias')"
|
:hint="t('mediaserver.serverAlias')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -352,6 +370,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.hostHint')"
|
:hint="t('mediaserver.hostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -362,10 +381,16 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.playHostHint')"
|
:hint="t('mediaserver.playHostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-play-network"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField v-model="mediaServerInfo.config.username" :label="t('mediaserver.username')" active />
|
<VTextField
|
||||||
|
v-model="mediaServerInfo.config.username"
|
||||||
|
:label="t('mediaserver.username')"
|
||||||
|
active
|
||||||
|
prepend-inner-icon="mdi-account"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField
|
<VTextField
|
||||||
@@ -373,6 +398,7 @@ onMounted(() => {
|
|||||||
v-model="mediaServerInfo.config.password"
|
v-model="mediaServerInfo.config.password"
|
||||||
:label="t('mediaserver.password')"
|
:label="t('mediaserver.password')"
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -387,6 +413,7 @@ onMounted(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
append-inner-icon="mdi-refresh"
|
append-inner-icon="mdi-refresh"
|
||||||
|
prepend-inner-icon="mdi-library"
|
||||||
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -400,6 +427,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.serverAlias')"
|
:hint="t('mediaserver.serverAlias')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -410,6 +438,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.hostHint')"
|
:hint="t('mediaserver.hostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -420,6 +449,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.playHostHint')"
|
:hint="t('mediaserver.playHostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-play-network"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -429,6 +459,7 @@ onMounted(() => {
|
|||||||
:hint="t('mediaserver.plexTokenHint')"
|
:hint="t('mediaserver.plexTokenHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -443,6 +474,7 @@ onMounted(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
append-inner-icon="mdi-refresh"
|
append-inner-icon="mdi-refresh"
|
||||||
|
prepend-inner-icon="mdi-library"
|
||||||
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -454,10 +486,16 @@ onMounted(() => {
|
|||||||
:label="t('mediaserver.type')"
|
:label="t('mediaserver.type')"
|
||||||
:hint="t('mediaserver.customTypeHint')"
|
:hint="t('mediaserver.customTypeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-cog"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField :label="t('common.name')" :hint="t('mediaserver.nameRequired')" persistent-hint />
|
<VTextField
|
||||||
|
:label="t('common.name')"
|
||||||
|
:hint="t('mediaserver.nameRequired')"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VForm>
|
</VForm>
|
||||||
|
|||||||
@@ -148,8 +148,15 @@ function onClose() {
|
|||||||
max-width="40rem"
|
max-width="40rem"
|
||||||
:fullscreen="!display.mdAndUp.value"
|
:fullscreen="!display.mdAndUp.value"
|
||||||
>
|
>
|
||||||
<VCard :title="`${props.notification.name} - ${t('notification.config')}`">
|
<VCard>
|
||||||
<VDialogCloseBtn v-model="notificationInfoDialog" />
|
<VCardItem class="py-2">
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-cog" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ t('common.config') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ props.notification.name }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
|
<VDialogCloseBtn @click="notificationInfoDialog = false" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm>
|
<VForm>
|
||||||
@@ -167,6 +174,7 @@ function onClose() {
|
|||||||
clearable
|
clearable
|
||||||
chips
|
chips
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-bell-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -178,6 +186,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.name')"
|
:placeholder="t('notification.name')"
|
||||||
:hint="t('notification.nameHint')"
|
:hint="t('notification.nameHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -186,6 +195,7 @@ function onClose() {
|
|||||||
:label="t('notification.wechat.corpId')"
|
:label="t('notification.wechat.corpId')"
|
||||||
:hint="t('notification.wechat.corpIdHint')"
|
:hint="t('notification.wechat.corpIdHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-domain"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -194,6 +204,7 @@ function onClose() {
|
|||||||
:label="t('notification.wechat.appId')"
|
:label="t('notification.wechat.appId')"
|
||||||
:hint="t('notification.wechat.appIdHint')"
|
:hint="t('notification.wechat.appIdHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-application"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -202,6 +213,7 @@ function onClose() {
|
|||||||
:label="t('notification.wechat.appSecret')"
|
:label="t('notification.wechat.appSecret')"
|
||||||
:hint="t('notification.wechat.appSecretHint')"
|
:hint="t('notification.wechat.appSecretHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -210,6 +222,7 @@ function onClose() {
|
|||||||
:label="t('notification.wechat.proxy')"
|
:label="t('notification.wechat.proxy')"
|
||||||
:hint="t('notification.wechat.proxyHint')"
|
:hint="t('notification.wechat.proxyHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-server-network"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -218,6 +231,7 @@ function onClose() {
|
|||||||
:label="t('notification.wechat.token')"
|
:label="t('notification.wechat.token')"
|
||||||
:hint="t('notification.wechat.tokenHint')"
|
:hint="t('notification.wechat.tokenHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key-variant"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -226,6 +240,7 @@ function onClose() {
|
|||||||
:label="t('notification.wechat.encodingAesKey')"
|
:label="t('notification.wechat.encodingAesKey')"
|
||||||
:hint="t('notification.wechat.encodingAesKeyHint')"
|
:hint="t('notification.wechat.encodingAesKeyHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -235,6 +250,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.wechat.adminsPlaceholder')"
|
:placeholder="t('notification.wechat.adminsPlaceholder')"
|
||||||
:hint="t('notification.wechat.adminsHint')"
|
:hint="t('notification.wechat.adminsHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account-supervisor"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -246,6 +262,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.name')"
|
:placeholder="t('notification.name')"
|
||||||
:hint="t('notification.nameHint')"
|
:hint="t('notification.nameHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -254,6 +271,7 @@ function onClose() {
|
|||||||
:label="t('notification.telegram.token')"
|
:label="t('notification.telegram.token')"
|
||||||
:hint="t('notification.telegram.tokenHint')"
|
:hint="t('notification.telegram.tokenHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -262,6 +280,7 @@ function onClose() {
|
|||||||
:label="t('notification.telegram.chatId')"
|
:label="t('notification.telegram.chatId')"
|
||||||
:hint="t('notification.telegram.chatIdHint')"
|
:hint="t('notification.telegram.chatIdHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-chat"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -271,6 +290,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.telegram.usersPlaceholder')"
|
:placeholder="t('notification.telegram.usersPlaceholder')"
|
||||||
:hint="t('notification.telegram.usersHint')"
|
:hint="t('notification.telegram.usersHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account-group"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -280,6 +300,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.telegram.adminsPlaceholder')"
|
:placeholder="t('notification.telegram.adminsPlaceholder')"
|
||||||
:hint="t('notification.telegram.adminsHint')"
|
:hint="t('notification.telegram.adminsHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account-supervisor"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -291,6 +312,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.name')"
|
:placeholder="t('notification.name')"
|
||||||
:hint="t('notification.nameHint')"
|
:hint="t('notification.nameHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -300,6 +322,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.slack.oauthTokenPlaceholder')"
|
:placeholder="t('notification.slack.oauthTokenPlaceholder')"
|
||||||
:hint="t('notification.slack.oauthTokenHint')"
|
:hint="t('notification.slack.oauthTokenHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -309,6 +332,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.slack.appTokenPlaceholder')"
|
:placeholder="t('notification.slack.appTokenPlaceholder')"
|
||||||
:hint="t('notification.slack.appTokenHint')"
|
:hint="t('notification.slack.appTokenHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-application"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -318,6 +342,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.slack.channelPlaceholder')"
|
:placeholder="t('notification.slack.channelPlaceholder')"
|
||||||
:hint="t('notification.slack.channelHint')"
|
:hint="t('notification.slack.channelHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-pound"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -329,6 +354,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.name')"
|
:placeholder="t('notification.name')"
|
||||||
:hint="t('notification.nameHint')"
|
:hint="t('notification.nameHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -337,6 +363,7 @@ function onClose() {
|
|||||||
:label="t('notification.synologychat.webhook')"
|
:label="t('notification.synologychat.webhook')"
|
||||||
:hint="t('notification.synologychat.webhookHint')"
|
:hint="t('notification.synologychat.webhookHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-webhook"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -345,6 +372,7 @@ function onClose() {
|
|||||||
:label="t('notification.synologychat.token')"
|
:label="t('notification.synologychat.token')"
|
||||||
:hint="t('notification.synologychat.tokenHint')"
|
:hint="t('notification.synologychat.tokenHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -356,6 +384,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.name')"
|
:placeholder="t('notification.name')"
|
||||||
:hint="t('notification.nameHint')"
|
:hint="t('notification.nameHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -364,6 +393,7 @@ function onClose() {
|
|||||||
:label="t('notification.vocechat.host')"
|
:label="t('notification.vocechat.host')"
|
||||||
:hint="t('notification.vocechat.hostHint')"
|
:hint="t('notification.vocechat.hostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -372,6 +402,7 @@ function onClose() {
|
|||||||
:label="t('notification.vocechat.apiKey')"
|
:label="t('notification.vocechat.apiKey')"
|
||||||
:hint="t('notification.vocechat.apiKeyHint')"
|
:hint="t('notification.vocechat.apiKeyHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -381,6 +412,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.vocechat.channelIdPlaceholder')"
|
:placeholder="t('notification.vocechat.channelIdPlaceholder')"
|
||||||
:hint="t('notification.vocechat.channelIdHint')"
|
:hint="t('notification.vocechat.channelIdHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-pound"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -392,6 +424,7 @@ function onClose() {
|
|||||||
:placeholder="t('notification.name')"
|
:placeholder="t('notification.name')"
|
||||||
:hint="t('notification.nameHint')"
|
:hint="t('notification.nameHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -400,6 +433,7 @@ function onClose() {
|
|||||||
:label="t('notification.webpush.username')"
|
:label="t('notification.webpush.username')"
|
||||||
:hint="t('notification.webpush.usernameHint')"
|
:hint="t('notification.webpush.usernameHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -411,6 +445,7 @@ function onClose() {
|
|||||||
:hint="t('notification.customTypeHint')"
|
:hint="t('notification.customTypeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-cog"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -419,7 +454,7 @@ function onClose() {
|
|||||||
:label="t('notification.name')"
|
:label="t('notification.name')"
|
||||||
:hint="t('notification.nameRequired')"
|
:hint="t('notification.nameRequired')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
prepend-inner-icon="mdi-label"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ const dropdownItems = ref([
|
|||||||
class="flex flex-col align-self-baseline justify-between px-2 py-2 w-full overflow-hidden max-h-10 min-h-10"
|
class="flex flex-col align-self-baseline justify-between px-2 py-2 w-full overflow-hidden max-h-10 min-h-10"
|
||||||
>
|
>
|
||||||
<div class="flex flex-nowrap items-center w-full pe-10">
|
<div class="flex flex-nowrap items-center w-full pe-10">
|
||||||
<div class="flex flex-nowrap max-w-32 items-center align-middle">
|
<div class="flex flex-nowrap max-w-40 items-center align-middle">
|
||||||
<VIcon icon="mdi-github" class="me-1" />
|
<VIcon icon="mdi-github" class="me-1" />
|
||||||
<a
|
<a
|
||||||
class="overflow-hidden text-ellipsis whitespace-nowrap"
|
class="overflow-hidden text-ellipsis whitespace-nowrap"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import VersionHistory from '@/components/misc/VersionHistory.vue'
|
|||||||
import ProgressDialog from '../dialog/ProgressDialog.vue'
|
import ProgressDialog from '../dialog/ProgressDialog.vue'
|
||||||
import PluginConfigDialog from '../dialog/PluginConfigDialog.vue'
|
import PluginConfigDialog from '../dialog/PluginConfigDialog.vue'
|
||||||
import PluginDataDialog from '../dialog/PluginDataDialog.vue'
|
import PluginDataDialog from '../dialog/PluginDataDialog.vue'
|
||||||
|
import LoggingView from '@/views/system/LoggingView.vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
|
|
||||||
@@ -58,6 +59,9 @@ const progressDialog = ref(false)
|
|||||||
// 插件数据页面
|
// 插件数据页面
|
||||||
const pluginInfoDialog = ref(false)
|
const pluginInfoDialog = ref(false)
|
||||||
|
|
||||||
|
// 实时日志弹窗
|
||||||
|
const loggingDialog = ref(false)
|
||||||
|
|
||||||
// 进度框文本
|
// 进度框文本
|
||||||
const progressText = ref('正在更新插件...')
|
const progressText = ref('正在更新插件...')
|
||||||
|
|
||||||
@@ -73,6 +77,18 @@ const imageLoadError = ref(false)
|
|||||||
// 更新日志弹窗
|
// 更新日志弹窗
|
||||||
const releaseDialog = ref(false)
|
const releaseDialog = ref(false)
|
||||||
|
|
||||||
|
// 插件分身对话框
|
||||||
|
const pluginCloneDialog = ref(false)
|
||||||
|
|
||||||
|
// 插件分身表单
|
||||||
|
const cloneForm = ref({
|
||||||
|
suffix: '',
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
version: '',
|
||||||
|
icon: '',
|
||||||
|
})
|
||||||
|
|
||||||
// 监听动作标识,如为true则打开详情
|
// 监听动作标识,如为true则打开详情
|
||||||
watch(
|
watch(
|
||||||
() => props.action,
|
() => props.action,
|
||||||
@@ -124,7 +140,12 @@ async function uninstallPlugin() {
|
|||||||
// 通知父组件刷新
|
// 通知父组件刷新
|
||||||
emit('remove')
|
emit('remove')
|
||||||
} else {
|
} else {
|
||||||
$toast.error(t('plugin.uninstallFailed', { name: props.plugin?.plugin_name, message: result.message }))
|
$toast.error(
|
||||||
|
t('plugin.uninstallFailed', {
|
||||||
|
name: props.plugin?.plugin_name,
|
||||||
|
message: result.message,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -178,7 +199,12 @@ async function resetPlugin() {
|
|||||||
// 通知父组件刷新
|
// 通知父组件刷新
|
||||||
emit('save')
|
emit('save')
|
||||||
} else {
|
} else {
|
||||||
$toast.error(t('plugin.resetFailed', { name: props.plugin?.plugin_name, message: result.message }))
|
$toast.error(
|
||||||
|
t('plugin.resetFailed', {
|
||||||
|
name: props.plugin?.plugin_name,
|
||||||
|
message: result.message,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -209,7 +235,12 @@ async function updatePlugin() {
|
|||||||
// 通知父组件刷新
|
// 通知父组件刷新
|
||||||
emit('save')
|
emit('save')
|
||||||
} else {
|
} else {
|
||||||
$toast.error(t('plugin.updateFailed', { name: props.plugin?.plugin_name, message: result.message }))
|
$toast.error(
|
||||||
|
t('plugin.updateFailed', {
|
||||||
|
name: props.plugin?.plugin_name,
|
||||||
|
message: result.message,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -241,6 +272,54 @@ function configDone() {
|
|||||||
emit('save')
|
emit('save')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示插件分身对话框
|
||||||
|
function showPluginClone() {
|
||||||
|
cloneForm.value = {
|
||||||
|
suffix: '',
|
||||||
|
name: t('plugin.cloneDefaultName', { name: props.plugin?.plugin_name }),
|
||||||
|
description: t('plugin.cloneDefaultDescription', { description: props.plugin?.plugin_desc }),
|
||||||
|
version: props.plugin?.plugin_version || '1.0',
|
||||||
|
icon: props.plugin?.plugin_icon || '',
|
||||||
|
}
|
||||||
|
pluginCloneDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行插件分身
|
||||||
|
async function executePluginClone() {
|
||||||
|
if (!cloneForm.value.suffix.trim()) {
|
||||||
|
$toast.error(t('plugin.suffixRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
progressDialog.value = true
|
||||||
|
progressText.value = t('plugin.cloning', { name: props.plugin?.plugin_name })
|
||||||
|
|
||||||
|
const result: { [key: string]: any } = await api.post(`plugin/clone/${props.plugin?.id}`, {
|
||||||
|
suffix: cloneForm.value.suffix.trim(),
|
||||||
|
name: cloneForm.value.name.trim(),
|
||||||
|
description: cloneForm.value.description.trim(),
|
||||||
|
version: cloneForm.value.version.trim(),
|
||||||
|
icon: cloneForm.value.icon.trim(),
|
||||||
|
})
|
||||||
|
|
||||||
|
progressDialog.value = false
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
$toast.success(t('plugin.cloneSuccess', { name: cloneForm.value.name }))
|
||||||
|
pluginCloneDialog.value = false
|
||||||
|
// 通知父组件刷新
|
||||||
|
emit('remove')
|
||||||
|
} else {
|
||||||
|
$toast.error(t('plugin.cloneFailed', { message: result.message }))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
progressDialog.value = false
|
||||||
|
$toast.error(t('plugin.cloneFailedGeneral'))
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 弹出菜单
|
// 弹出菜单
|
||||||
const dropdownItems = ref([
|
const dropdownItems = ref([
|
||||||
{
|
{
|
||||||
@@ -261,6 +340,16 @@ const dropdownItems = ref([
|
|||||||
click: showPluginConfig,
|
click: showPluginConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('plugin.clone'),
|
||||||
|
value: 8,
|
||||||
|
show: true,
|
||||||
|
props: {
|
||||||
|
prependIcon: 'mdi-content-copy',
|
||||||
|
color: 'info',
|
||||||
|
click: showPluginClone,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('plugin.update'),
|
title: t('plugin.update'),
|
||||||
value: 3,
|
value: 3,
|
||||||
@@ -298,7 +387,7 @@ const dropdownItems = ref([
|
|||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-file-document-outline',
|
prependIcon: 'mdi-file-document-outline',
|
||||||
click: () => {
|
click: () => {
|
||||||
openLoggerWindow()
|
loggingDialog.value = true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -366,7 +455,7 @@ watch(
|
|||||||
{{ props.plugin?.plugin_desc }}
|
{{ props.plugin?.plugin_desc }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative flex-shrink-0 self-center cursor-move pb-3">
|
<div class="relative flex-shrink-0 self-center pb-3" :class="{ 'cursor-move': display.mdAndUp.value }">
|
||||||
<VAvatar size="48">
|
<VAvatar size="48">
|
||||||
<VImg
|
<VImg
|
||||||
ref="imageRef"
|
ref="imageRef"
|
||||||
@@ -384,7 +473,7 @@ watch(
|
|||||||
class="flex flex-col align-self-baseline justify-between px-2 py-2 w-full overflow-hidden max-h-10 min-h-10"
|
class="flex flex-col align-self-baseline justify-between px-2 py-2 w-full overflow-hidden max-h-10 min-h-10"
|
||||||
>
|
>
|
||||||
<div class="flex flex-nowrap items-center w-full pe-10">
|
<div class="flex flex-nowrap items-center w-full pe-10">
|
||||||
<div class="flex flex-nowrap max-w-32 items-center align-middle">
|
<div class="flex flex-nowrap max-w-40 items-center align-middle">
|
||||||
<VImg :src="authorPath" class="author-avatar" @load="isAvatarLoaded = true">
|
<VImg :src="authorPath" class="author-avatar" @load="isAvatarLoaded = true">
|
||||||
<VIcon v-if="!isAvatarLoaded" size="small" icon="mdi-github" class="me-1" />
|
<VIcon v-if="!isAvatarLoaded" size="small" icon="mdi-github" class="me-1" />
|
||||||
</VImg>
|
</VImg>
|
||||||
@@ -470,6 +559,144 @@ watch(
|
|||||||
</VCardItem>
|
</VCardItem>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
|
||||||
|
<!-- 实时日志弹窗 -->
|
||||||
|
<VDialog
|
||||||
|
v-if="loggingDialog"
|
||||||
|
v-model="loggingDialog"
|
||||||
|
scrollable
|
||||||
|
max-width="60rem"
|
||||||
|
:fullscreen="!display.mdAndUp.value"
|
||||||
|
>
|
||||||
|
<VCard>
|
||||||
|
<VDialogCloseBtn @click="loggingDialog = false" />
|
||||||
|
<VCardItem>
|
||||||
|
<VCardTitle class="d-inline-flex">
|
||||||
|
<VIcon icon="mdi-file-document" class="me-2" />
|
||||||
|
{{ t('plugin.logTitle') }}
|
||||||
|
<a class="mx-2 d-inline-flex align-center cursor-pointer" @click="openLoggerWindow">
|
||||||
|
<VChip color="grey-darken-1" size="small" class="ml-2">
|
||||||
|
<VIcon icon="mdi-open-in-new" size="small" start />
|
||||||
|
{{ t('common.openInNewWindow') }}
|
||||||
|
</VChip>
|
||||||
|
</a>
|
||||||
|
</VCardTitle>
|
||||||
|
</VCardItem>
|
||||||
|
<VDivider />
|
||||||
|
<VCardText>
|
||||||
|
<LoggingView :logfile="`plugins/${props.plugin?.id?.toLowerCase()}.log`" />
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
|
|
||||||
|
<!-- 插件分身对话框 -->
|
||||||
|
<VDialog
|
||||||
|
v-if="pluginCloneDialog"
|
||||||
|
v-model="pluginCloneDialog"
|
||||||
|
width="600"
|
||||||
|
scrollable
|
||||||
|
:fullscreen="!display.mdAndUp.value"
|
||||||
|
>
|
||||||
|
<VCard>
|
||||||
|
<VCardItem class="py-2">
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-content-copy" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ t('plugin.cloneTitle') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ t('plugin.cloneSubtitle', { name: props.plugin?.plugin_name }) }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
|
<VDialogCloseBtn @click="pluginCloneDialog = false" />
|
||||||
|
<VDivider />
|
||||||
|
<VCardText>
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="cloneForm.suffix"
|
||||||
|
:label="t('plugin.suffix') + ' *'"
|
||||||
|
:placeholder="t('plugin.suffixPlaceholder')"
|
||||||
|
:hint="t('plugin.suffixHint')"
|
||||||
|
persistent-hint
|
||||||
|
:rules="[
|
||||||
|
v => !!v || t('plugin.suffixRequired'),
|
||||||
|
v => /^[a-zA-Z0-9]+$/.test(v) || t('plugin.suffixFormatError'),
|
||||||
|
v => v.length <= 20 || t('plugin.suffixLengthError'),
|
||||||
|
]"
|
||||||
|
required
|
||||||
|
prepend-inner-icon="mdi-tag"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="cloneForm.name"
|
||||||
|
:label="t('plugin.cloneName')"
|
||||||
|
:placeholder="t('plugin.cloneNamePlaceholder')"
|
||||||
|
:hint="t('plugin.cloneNameHint')"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-rename-box"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12">
|
||||||
|
<VTextField
|
||||||
|
v-model="cloneForm.description"
|
||||||
|
:label="t('plugin.cloneDescriptionLabel')"
|
||||||
|
:placeholder="t('plugin.cloneDescriptionPlaceholder')"
|
||||||
|
:hint="t('plugin.cloneDescriptionHint')"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-text"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="cloneForm.version"
|
||||||
|
:label="t('plugin.cloneVersion')"
|
||||||
|
:placeholder="t('plugin.cloneVersionPlaceholder')"
|
||||||
|
:hint="t('plugin.cloneVersionHint')"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-numeric"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="cloneForm.icon"
|
||||||
|
:label="t('plugin.cloneIcon')"
|
||||||
|
:placeholder="t('plugin.cloneIconPlaceholder')"
|
||||||
|
:hint="t('plugin.cloneIconHint')"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-image"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<!-- 重要提醒 -->
|
||||||
|
<VCol cols="12">
|
||||||
|
<VAlert type="warning" variant="tonal" density="compact" class="mt-2" icon="mdi-alert-circle-outline">
|
||||||
|
<div class="text-body-2">
|
||||||
|
<strong>{{ t('common.notice') }}</strong
|
||||||
|
>:{{ t('plugin.cloneNotice') }}
|
||||||
|
</div>
|
||||||
|
</VAlert>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
<VCardActions class="pt-3">
|
||||||
|
<VSpacer />
|
||||||
|
<VBtn
|
||||||
|
color="primary"
|
||||||
|
@click="executePluginClone"
|
||||||
|
prepend-icon="mdi-content-copy"
|
||||||
|
class="px-5"
|
||||||
|
:disabled="!cloneForm.suffix.trim()"
|
||||||
|
>
|
||||||
|
{{ t('plugin.createClone') }}
|
||||||
|
</VBtn>
|
||||||
|
</VCardActions>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -298,13 +298,18 @@ const dropdownItems = ref([
|
|||||||
<div class="plugin-folder-card__body" :class="{ 'plugin-folder-card__body--no-icon': !shouldShowIcon }">
|
<div class="plugin-folder-card__body" :class="{ 'plugin-folder-card__body--no-icon': !shouldShowIcon }">
|
||||||
<!-- 文件夹图标 -->
|
<!-- 文件夹图标 -->
|
||||||
<div v-if="shouldShowIcon" class="plugin-folder-card__icon-container">
|
<div v-if="shouldShowIcon" class="plugin-folder-card__icon-container">
|
||||||
<VIcon :icon="folderIcon" :size="display.mobile ? 56 : 72" class="cursor-move" :color="iconColor" />
|
<VIcon
|
||||||
|
:icon="folderIcon"
|
||||||
|
:size="display.mobile ? 56 : 72"
|
||||||
|
:color="iconColor"
|
||||||
|
:class="{ 'cursor-move': display.mdAndUp.value }"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 文件夹信息 -->
|
<!-- 文件夹信息 -->
|
||||||
<div
|
<div
|
||||||
class="plugin-folder-card__info cursor-move"
|
class="plugin-folder-card__info"
|
||||||
:class="{ 'plugin-folder-card__info--no-icon': !shouldShowIcon }"
|
:class="{ 'cursor-move': display.mdAndUp.value, 'plugin-folder-card__info--no-icon': !shouldShowIcon }"
|
||||||
>
|
>
|
||||||
<!-- 文件夹名称 -->
|
<!-- 文件夹名称 -->
|
||||||
<h3 class="plugin-folder-card__name">
|
<h3 class="plugin-folder-card__name">
|
||||||
@@ -347,10 +352,13 @@ const dropdownItems = ref([
|
|||||||
<!-- 重命名对话框 -->
|
<!-- 重命名对话框 -->
|
||||||
<VDialog v-if="renameDialog" v-model="renameDialog" max-width="400">
|
<VDialog v-if="renameDialog" v-model="renameDialog" max-width="400">
|
||||||
<VCard>
|
<VCard>
|
||||||
<VDialogCloseBtn @click="renameDialog = false" />
|
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-pencil" class="me-2" />
|
||||||
|
</template>
|
||||||
<VCardTitle>{{ t('folder.renameFolder') }}</VCardTitle>
|
<VCardTitle>{{ t('folder.renameFolder') }}</VCardTitle>
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
|
<VDialogCloseBtn @click="renameDialog = false" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VTextField
|
<VTextField
|
||||||
@@ -462,6 +470,7 @@ const dropdownItems = ref([
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
:hint="t('folder.customBackgroundImageHint')"
|
:hint="t('folder.customBackgroundImageHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-image"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
183
src/components/cards/PluginMixedSortCard.vue
Normal file
183
src/components/cards/PluginMixedSortCard.vue
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import PluginCard from './PluginCard.vue'
|
||||||
|
import PluginFolderCard from './PluginFolderCard.vue'
|
||||||
|
|
||||||
|
interface MixedSortItem {
|
||||||
|
type: 'folder' | 'plugin'
|
||||||
|
id: string
|
||||||
|
data: any
|
||||||
|
order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
item: MixedSortItem
|
||||||
|
pluginStatistics?: { [key: string]: number }
|
||||||
|
pluginActions?: { [key: string]: boolean }
|
||||||
|
showRemoveButton?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
pluginStatistics: () => ({}),
|
||||||
|
pluginActions: () => ({}),
|
||||||
|
showRemoveButton: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
openFolder: [folderName: string]
|
||||||
|
deleteFolder: [folderName: string]
|
||||||
|
renameFolder: [oldName: string, newName: string]
|
||||||
|
updateFolderConfig: [folderName: string, config: any]
|
||||||
|
refreshData: []
|
||||||
|
actionDone: [pluginId: string]
|
||||||
|
removeFromFolder: [pluginId: string]
|
||||||
|
dropToFolder: [event: DragEvent, folderName: string]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 拖拽事件处理
|
||||||
|
function handleDragOver(event: DragEvent) {
|
||||||
|
// 只有当拖拽的是插件时才允许放入文件夹
|
||||||
|
if (props.item.type === 'folder') {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
event.dataTransfer!.dropEffect = 'move'
|
||||||
|
const target = event.currentTarget as HTMLElement
|
||||||
|
target.classList.add('drag-over')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragEnter(event: DragEvent) {
|
||||||
|
if (props.item.type === 'folder') {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragLeave(event: DragEvent) {
|
||||||
|
if (props.item.type === 'folder') {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
const target = event.currentTarget as HTMLElement
|
||||||
|
target.classList.remove('drag-over')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDropToFolder(event: DragEvent) {
|
||||||
|
if (props.item.type === 'folder') {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
const target = event.currentTarget as HTMLElement
|
||||||
|
target.classList.remove('drag-over')
|
||||||
|
|
||||||
|
emit('dropToFolder', event, props.item.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mixed-sort-card-wrapper h-full">
|
||||||
|
<!-- 文件夹卡片 -->
|
||||||
|
<div
|
||||||
|
v-if="item.type === 'folder'"
|
||||||
|
class="drop-zone h-full"
|
||||||
|
:data-plugin-id="item.id"
|
||||||
|
@dragover="handleDragOver"
|
||||||
|
@dragenter="handleDragEnter"
|
||||||
|
@dragleave="handleDragLeave"
|
||||||
|
@drop="handleDropToFolder"
|
||||||
|
>
|
||||||
|
<PluginFolderCard
|
||||||
|
:folder-name="item.data.name"
|
||||||
|
:plugin-count="item.data.pluginCount"
|
||||||
|
:folder-config="item.data.config"
|
||||||
|
@open="$emit('openFolder', item.id)"
|
||||||
|
@delete="$emit('deleteFolder', item.id)"
|
||||||
|
@rename="(oldName, newName) => $emit('renameFolder', oldName, newName)"
|
||||||
|
@update-config="(folderName, config) => $emit('updateFolderConfig', folderName, config)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 插件卡片 -->
|
||||||
|
<div v-else-if="item.type === 'plugin'" class="plugin-item-wrapper h-full" :data-plugin-id="item.id">
|
||||||
|
<PluginCard
|
||||||
|
:count="pluginStatistics[item.id] || 0"
|
||||||
|
:plugin="item.data"
|
||||||
|
:action="pluginActions[item.id] || false"
|
||||||
|
@remove="$emit('refreshData')"
|
||||||
|
@save="$emit('refreshData')"
|
||||||
|
@action-done="$emit('actionDone', item.id)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 移出文件夹按钮(仅在文件夹内显示) -->
|
||||||
|
<VBtn
|
||||||
|
v-if="showRemoveButton"
|
||||||
|
icon="mdi-folder-remove"
|
||||||
|
variant="text"
|
||||||
|
color="warning"
|
||||||
|
size="small"
|
||||||
|
class="remove-from-folder-btn"
|
||||||
|
@click="$emit('removeFromFolder', item.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mixed-sort-card-wrapper {
|
||||||
|
block-size: 100%;
|
||||||
|
inline-size: 100%;
|
||||||
|
|
||||||
|
// 确保拖拽时的边界清晰
|
||||||
|
&.sortable-chosen {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sortable-ghost {
|
||||||
|
border: 2px dashed #2196f3;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(33, 150, 243, 10%);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽相关样式
|
||||||
|
.drop-zone {
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate; // 创建新的层叠上下文
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.drag-over {
|
||||||
|
border: 2px dashed #2196f3;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 0 20px rgba(33, 150, 243, 50%);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-item-wrapper {
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate; // 创建新的层叠上下文
|
||||||
|
|
||||||
|
.remove-from-folder-btn {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 50%;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
background: rgba(255, 255, 255, 10%);
|
||||||
|
inset-block-start: 4px;
|
||||||
|
inset-inline-end: 4px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .remove-from-folder-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽时的样式优化
|
||||||
|
.mixed-sort-card-wrapper.sortable-drag {
|
||||||
|
.remove-from-folder-btn {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -213,6 +213,9 @@ function onClose() {
|
|||||||
>
|
>
|
||||||
<VCard>
|
<VCard>
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-cog" />
|
||||||
|
</template>
|
||||||
<VCardTitle>{{ t('storage.custom') }}</VCardTitle>
|
<VCardTitle>{{ t('storage.custom') }}</VCardTitle>
|
||||||
<VDialogCloseBtn v-model="customConfigDialog" />
|
<VDialogCloseBtn v-model="customConfigDialog" />
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
@@ -225,11 +228,16 @@ function onClose() {
|
|||||||
:label="t('storage.type')"
|
:label="t('storage.type')"
|
||||||
:hint="t('storage.customTypeHint')"
|
:hint="t('storage.customTypeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
active
|
prepend-inner-icon="mdi-database"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField v-model="customName" :label="t('storage.name')" persistent-hint active />
|
<VTextField
|
||||||
|
v-model="customName"
|
||||||
|
:label="t('storage.name')"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-label"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ function onSubscribeEditRemove() {
|
|||||||
<div>
|
<div>
|
||||||
<VCardText class="flex items-center pt-3 pb-2">
|
<VCardText class="flex items-center pt-3 pb-2">
|
||||||
<div
|
<div
|
||||||
class="h-auto w-14 flex-shrink-0 overflow-hidden rounded-md"
|
class="h-auto w-12 flex-shrink-0 overflow-hidden rounded-md"
|
||||||
v-if="imageLoaded"
|
v-if="imageLoaded"
|
||||||
:class="{ 'cursor-move': display.mdAndUp.value }"
|
:class="{ 'cursor-move': display.mdAndUp.value }"
|
||||||
>
|
>
|
||||||
@@ -367,7 +367,7 @@ function onSubscribeEditRemove() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col justify-center overflow-hidden pl-2 xl:pl-4">
|
<div class="flex flex-col justify-center overflow-hidden pl-2 xl:pl-4">
|
||||||
<div class="text-sm font-medium text-white sm:pt-1">{{ props.media?.year }}</div>
|
<div class="text-sm font-medium text-white sm:pt-1">{{ props.media?.year }}</div>
|
||||||
<div class="mr-2 min-w-0 text-lg font-bold text-white">
|
<div class="mr-2 min-w-0 text-lg font-bold text-white text-ellipsis overflow-hidden line-clamp-2 ...">
|
||||||
{{ props.media?.name }}
|
{{ props.media?.name }}
|
||||||
{{ formatSeason(props.media?.season ? props.media?.season.toString() : '') }}
|
{{ formatSeason(props.media?.season ? props.media?.season.toString() : '') }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -179,7 +179,13 @@ const resolveProgress = (item: Workflow) => {
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:class="{ 'transition transform-cpu duration-300 -translate-y-1': hover.isHovering }"
|
:class="{ 'transition transform-cpu duration-300 -translate-y-1': hover.isHovering }"
|
||||||
>
|
>
|
||||||
<VCardItem class="py-3" :class="`bg-${resolveStatusVariant(workflow?.state).color}`">
|
<VCardItem
|
||||||
|
:class="{
|
||||||
|
'py-1': workflow?.description,
|
||||||
|
'py-3': !workflow?.description,
|
||||||
|
[`bg-${resolveStatusVariant(workflow?.state).color}`]: true,
|
||||||
|
}"
|
||||||
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<VAvatar variant="text" class="me-2">
|
<VAvatar variant="text" class="me-2">
|
||||||
<VIcon
|
<VIcon
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ import type { DownloaderConf, MediaInfo, TorrentInfo, TransferDirectoryConf } fr
|
|||||||
import { formatFileSize } from '@/@core/utils/formatters'
|
import { formatFileSize } from '@/@core/utils/formatters'
|
||||||
import { VCardTitle, VChip } from 'vuetify/lib/components/index.mjs'
|
import { VCardTitle, VChip } from 'vuetify/lib/components/index.mjs'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useDisplay } from 'vuetify'
|
|
||||||
|
|
||||||
// 显示器宽度
|
|
||||||
const display = useDisplay()
|
|
||||||
|
|
||||||
// 多语言支持
|
// 多语言支持
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -136,71 +132,77 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VDialog max-width="35rem" scrollable :fullscreen="!display.mdAndUp.value">
|
<VDialog max-width="35rem" scrollable>
|
||||||
<VCard>
|
<VCard>
|
||||||
<VCardTitle class="py-4 me-12">
|
<VCardItem class="py-2">
|
||||||
<VIcon icon="mdi-download" class="me-2" />
|
<template #prepend>
|
||||||
<span v-if="title">{{ torrent?.site_name }} - {{ title }}</span>
|
<VIcon icon="mdi-monitor-arrow-down-variant" class="me-2" />
|
||||||
<span v-else>{{ t('dialog.addDownload.confirmDownload') }}</span>
|
</template>
|
||||||
</VCardTitle>
|
<VCardTitle>{{ t('dialog.addDownload.confirmDownload') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ torrent?.site_name }} - {{ title }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VList lines="one">
|
<VCardText>
|
||||||
<VListItem>
|
<VList lines="one">
|
||||||
<template #prepend>
|
<VListItem>
|
||||||
<VIcon icon="mdi-web"></VIcon>
|
<template #prepend>
|
||||||
</template>
|
<VIcon icon="mdi-web"></VIcon>
|
||||||
<VListItemTitle>
|
</template>
|
||||||
<span class="whitespace-break-spaces me-2">{{ torrent?.title }}</span>
|
<VListItemTitle>
|
||||||
<span class="text-green-700 ms-2 text-sm">↑{{ torrent?.seeders }}</span>
|
<span class="whitespace-break-spaces me-2">{{ torrent?.title }}</span>
|
||||||
<span class="text-orange-700 ms-2 text-sm">↓{{ torrent?.peers }}</span>
|
<span class="text-green-700 ms-2 text-sm">↑{{ torrent?.seeders }}</span>
|
||||||
</VListItemTitle>
|
<span class="text-orange-700 ms-2 text-sm">↓{{ torrent?.peers }}</span>
|
||||||
</VListItem>
|
</VListItemTitle>
|
||||||
<VListItem v-if="torrent?.description">
|
</VListItem>
|
||||||
<template #prepend>
|
<VListItem v-if="torrent?.description">
|
||||||
<VIcon icon="mdi-subtitles-outline"></VIcon>
|
<template #prepend>
|
||||||
</template>
|
<VIcon icon="mdi-subtitles-outline"></VIcon>
|
||||||
<VListItemTitle>
|
</template>
|
||||||
<span class="text-body-2 whitespace-break-spaces">{{ torrent?.description }}</span>
|
<VListItemTitle>
|
||||||
</VListItemTitle>
|
<span class="text-body-2 whitespace-break-spaces">{{ torrent?.description }}</span>
|
||||||
</VListItem>
|
</VListItemTitle>
|
||||||
<VListItem v-if="torrent?.size">
|
</VListItem>
|
||||||
<template #prepend>
|
<VListItem v-if="torrent?.size">
|
||||||
<VIcon icon="mdi-database"></VIcon>
|
<template #prepend>
|
||||||
</template>
|
<VIcon icon="mdi-database"></VIcon>
|
||||||
<VListItemTitle>
|
</template>
|
||||||
<span class="text-body-2">
|
<VListItemTitle>
|
||||||
<VChip variant="tonal" label>
|
<span class="text-body-2">
|
||||||
{{ formatFileSize(torrent?.size || 0) }}
|
<VChip variant="tonal" label>
|
||||||
</VChip>
|
{{ formatFileSize(torrent?.size || 0) }}
|
||||||
</span>
|
</VChip>
|
||||||
</VListItemTitle>
|
</span>
|
||||||
</VListItem>
|
</VListItemTitle>
|
||||||
</VList>
|
</VListItem>
|
||||||
<VRow class="px-7">
|
</VList>
|
||||||
<VCol cols="12" md="4">
|
<VRow class="px-5">
|
||||||
<VSelect
|
<VCol cols="12" md="6">
|
||||||
v-model="selectedDownloader"
|
<VSelect
|
||||||
:items="downloaderOptions"
|
v-model="selectedDownloader"
|
||||||
size="small"
|
:items="downloaderOptions"
|
||||||
:label="t('dialog.addDownload.downloader')"
|
size="small"
|
||||||
variant="underlined"
|
:label="t('dialog.addDownload.downloader')"
|
||||||
:placeholder="t('dialog.addDownload.defaultPlaceholder')"
|
variant="underlined"
|
||||||
density="compact"
|
:placeholder="t('dialog.addDownload.defaultPlaceholder')"
|
||||||
/>
|
density="comfortable"
|
||||||
</VCol>
|
prepend-inner-icon="mdi-download"
|
||||||
<VCol cols="12" md="8">
|
/>
|
||||||
<VCombobox
|
</VCol>
|
||||||
v-model="selectedDirectory"
|
<VCol cols="12" md="6">
|
||||||
:items="targetDirectories"
|
<VCombobox
|
||||||
:label="t('dialog.addDownload.saveDirectory')"
|
v-model="selectedDirectory"
|
||||||
size="small"
|
:items="targetDirectories"
|
||||||
:placeholder="t('dialog.addDownload.autoPlaceholder')"
|
:label="t('dialog.addDownload.saveDirectory')"
|
||||||
variant="underlined"
|
size="small"
|
||||||
density="compact"
|
:placeholder="t('dialog.addDownload.autoPlaceholder')"
|
||||||
/>
|
variant="underlined"
|
||||||
</VCol>
|
density="comfortable"
|
||||||
</VRow>
|
prepend-inner-icon="mdi-folder"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VCardText>
|
||||||
<VCardText class="text-center">
|
<VCardText class="text-center">
|
||||||
<VBtn variant="elevated" :disabled="loading" @click="addDownload" :prepend-icon="icon" class="px-5">
|
<VBtn variant="elevated" :disabled="loading" @click="addDownload" :prepend-icon="icon" class="px-5">
|
||||||
{{ buttonText }}
|
{{ buttonText }}
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ async function savaAlistConfig() {
|
|||||||
<VCard>
|
<VCard>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-cog-outline" class="me-2" />
|
||||||
|
</template>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
{{ t('dialog.alistConfig.title') }}
|
{{ t('dialog.alistConfig.title') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
@@ -87,6 +90,7 @@ async function savaAlistConfig() {
|
|||||||
:hint="t('dialog.alistConfig.serverUrl')"
|
:hint="t('dialog.alistConfig.serverUrl')"
|
||||||
:label="t('dialog.alistConfig.serverUrl')"
|
:label="t('dialog.alistConfig.serverUrl')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4">
|
<VCol cols="12" md="4">
|
||||||
@@ -96,6 +100,7 @@ async function savaAlistConfig() {
|
|||||||
:label="t('dialog.alistConfig.loginType')"
|
:label="t('dialog.alistConfig.loginType')"
|
||||||
:hint="t('dialog.alistConfig.loginType')"
|
:hint="t('dialog.alistConfig.loginType')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-login"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4" v-if="loginType == 'username'">
|
<VCol cols="12" md="4" v-if="loginType == 'username'">
|
||||||
@@ -104,6 +109,7 @@ async function savaAlistConfig() {
|
|||||||
:hint="t('dialog.alistConfig.username')"
|
:hint="t('dialog.alistConfig.username')"
|
||||||
:label="t('dialog.alistConfig.username')"
|
:label="t('dialog.alistConfig.username')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4" v-if="loginType == 'username'">
|
<VCol cols="12" md="4" v-if="loginType == 'username'">
|
||||||
@@ -113,6 +119,7 @@ async function savaAlistConfig() {
|
|||||||
:hint="t('dialog.alistConfig.password')"
|
:hint="t('dialog.alistConfig.password')"
|
||||||
:label="t('dialog.alistConfig.password')"
|
:label="t('dialog.alistConfig.password')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="8" v-if="loginType == 'token'">
|
<VCol cols="12" md="8" v-if="loginType == 'token'">
|
||||||
@@ -121,6 +128,7 @@ async function savaAlistConfig() {
|
|||||||
:hint="t('dialog.alistConfig.loginTypeOptions.token')"
|
:hint="t('dialog.alistConfig.loginTypeOptions.token')"
|
||||||
:label="t('dialog.alistConfig.loginTypeOptions.token')"
|
:label="t('dialog.alistConfig.loginTypeOptions.token')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -114,6 +114,9 @@ onUnmounted(() => {
|
|||||||
<VCard>
|
<VCard>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-qrcode" class="me-2" />
|
||||||
|
</template>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
{{ t('dialog.aliyunAuth.loginTitle') }}
|
{{ t('dialog.aliyunAuth.loginTitle') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
|
|||||||
@@ -25,10 +25,16 @@ function handleImport() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog width="40rem" scrollable max-height="85vh">
|
<VDialog width="40rem" scrollable max-height="85vh">
|
||||||
<VCard :title="props.title">
|
<VCard>
|
||||||
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-code-json" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ props.title }}</VCardTitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VCardText class="pt-2">
|
<VCardText class="pt-2">
|
||||||
<VTextarea v-model="codeString" />
|
<VTextarea v-model="codeString" prepend-inner-icon="mdi-code-json" />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardActions>
|
<VCardActions>
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ async function handleReset() {
|
|||||||
<VCard>
|
<VCard>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-cog-outline" class="me-2" />
|
||||||
|
</template>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
{{ t('dialog.rcloneConfig.title') }}
|
{{ t('dialog.rcloneConfig.title') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
@@ -69,7 +72,11 @@ async function handleReset() {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VTextField v-model="props.conf.filepath" :label="t('dialog.rcloneConfig.filePath')" />
|
<VTextField
|
||||||
|
v-model="props.conf.filepath"
|
||||||
|
:label="t('dialog.rcloneConfig.filePath')"
|
||||||
|
prepend-inner-icon="mdi-file-document"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VAceEditor
|
<VAceEditor
|
||||||
|
|||||||
@@ -82,15 +82,18 @@ const storageOptions = computed(() => {
|
|||||||
|
|
||||||
// 标题
|
// 标题
|
||||||
const dialogTitle = computed(() => {
|
const dialogTitle = computed(() => {
|
||||||
|
return t('dialog.reorganize.manualTitle')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 副标题
|
||||||
|
const dialogSubtitle = computed(() => {
|
||||||
if (props.items) {
|
if (props.items) {
|
||||||
if (props.items.length > 1) return t('dialog.reorganize.multipleItemsTitle', { count: props.items.length })
|
if (props.items.length > 1) return t('dialog.reorganize.multipleItemsTitle', { count: props.items.length })
|
||||||
return t('dialog.reorganize.singleItemTitle', { path: props.items[0].path })
|
return t('dialog.reorganize.singleItemTitle', { path: props.items[0].path })
|
||||||
} else if (props.logids) {
|
} else if (props.logids) {
|
||||||
return t('dialog.reorganize.multipleItemsTitle', { count: props.logids.length })
|
return t('dialog.reorganize.multipleItemsTitle', { count: props.logids.length })
|
||||||
}
|
}
|
||||||
return t('dialog.reorganize.manualTitle')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 禁用指定集数
|
// 禁用指定集数
|
||||||
const disableEpisodeDetail = computed(() => {
|
const disableEpisodeDetail = computed(() => {
|
||||||
if (props.items) {
|
if (props.items) {
|
||||||
@@ -250,7 +253,12 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard :title="dialogTitle">
|
<VCard>
|
||||||
|
<VCardItem class="py-2">
|
||||||
|
<template #prepend> <VIcon icon="mdi-folder-move" class="me-2" /> </template>
|
||||||
|
<VCardTitle>{{ dialogTitle }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ dialogSubtitle }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
@@ -264,6 +272,7 @@ onUnmounted(() => {
|
|||||||
:placeholder="t('dialog.reorganize.targetPathPlaceholder')"
|
:placeholder="t('dialog.reorganize.targetPathPlaceholder')"
|
||||||
:hint="t('dialog.reorganize.targetStorageHint')"
|
:hint="t('dialog.reorganize.targetStorageHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-harddisk"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -273,6 +282,7 @@ onUnmounted(() => {
|
|||||||
:items="transferTypeOptions"
|
:items="transferTypeOptions"
|
||||||
:hint="t('dialog.reorganize.transferTypeHint')"
|
:hint="t('dialog.reorganize.transferTypeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-swap-horizontal"
|
||||||
>
|
>
|
||||||
<template v-slot:selection="{ item }">
|
<template v-slot:selection="{ item }">
|
||||||
{{ transferForm.transfer_type === '' ? t('dialog.reorganize.auto') : item.title }}
|
{{ transferForm.transfer_type === '' ? t('dialog.reorganize.auto') : item.title }}
|
||||||
@@ -287,6 +297,7 @@ onUnmounted(() => {
|
|||||||
:placeholder="t('dialog.reorganize.targetPathPlaceholder')"
|
:placeholder="t('dialog.reorganize.targetPathPlaceholder')"
|
||||||
:hint="t('dialog.reorganize.targetPathHint')"
|
:hint="t('dialog.reorganize.targetPathHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-folder-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -302,6 +313,7 @@ onUnmounted(() => {
|
|||||||
]"
|
]"
|
||||||
:hint="t('dialog.reorganize.mediaTypeHint')"
|
:hint="t('dialog.reorganize.mediaTypeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-movie-open"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -315,6 +327,7 @@ onUnmounted(() => {
|
|||||||
append-inner-icon="mdi-magnify"
|
append-inner-icon="mdi-magnify"
|
||||||
:hint="t('dialog.reorganize.mediaIdHint')"
|
:hint="t('dialog.reorganize.mediaIdHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-identifier"
|
||||||
@click:append-inner="mediaSelectorDialog = true"
|
@click:append-inner="mediaSelectorDialog = true"
|
||||||
/>
|
/>
|
||||||
<VTextField
|
<VTextField
|
||||||
@@ -327,6 +340,7 @@ onUnmounted(() => {
|
|||||||
append-inner-icon="mdi-magnify"
|
append-inner-icon="mdi-magnify"
|
||||||
:hint="t('dialog.reorganize.mediaIdHint')"
|
:hint="t('dialog.reorganize.mediaIdHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-identifier"
|
||||||
@click:append-inner="mediaSelectorDialog = true"
|
@click:append-inner="mediaSelectorDialog = true"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -339,6 +353,7 @@ onUnmounted(() => {
|
|||||||
:placeholder="t('dialog.reorganize.episodeGroupPlaceholder')"
|
:placeholder="t('dialog.reorganize.episodeGroupPlaceholder')"
|
||||||
:hint="t('dialog.reorganize.episodeGroupHint')"
|
:hint="t('dialog.reorganize.episodeGroupHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-view-list"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="3">
|
<VCol cols="12" md="3">
|
||||||
@@ -348,6 +363,7 @@ onUnmounted(() => {
|
|||||||
:items="seasonItems"
|
:items="seasonItems"
|
||||||
:hint="t('dialog.reorganize.seasonHint')"
|
:hint="t('dialog.reorganize.seasonHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-calendar"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="3">
|
<VCol cols="12" md="3">
|
||||||
@@ -358,6 +374,7 @@ onUnmounted(() => {
|
|||||||
:placeholder="t('dialog.reorganize.episodeDetailPlaceholder')"
|
:placeholder="t('dialog.reorganize.episodeDetailPlaceholder')"
|
||||||
:hint="t('dialog.reorganize.episodeDetailHint')"
|
:hint="t('dialog.reorganize.episodeDetailHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-playlist-play"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -367,6 +384,7 @@ onUnmounted(() => {
|
|||||||
:placeholder="t('dialog.reorganize.episodeFormatPlaceholder')"
|
:placeholder="t('dialog.reorganize.episodeFormatPlaceholder')"
|
||||||
:hint="t('dialog.reorganize.episodeFormatHint')"
|
:hint="t('dialog.reorganize.episodeFormatHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-format-text"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -376,6 +394,7 @@ onUnmounted(() => {
|
|||||||
:placeholder="t('dialog.reorganize.episodeOffsetPlaceholder')"
|
:placeholder="t('dialog.reorganize.episodeOffsetPlaceholder')"
|
||||||
:hint="t('dialog.reorganize.episodeOffsetHint')"
|
:hint="t('dialog.reorganize.episodeOffsetHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-numeric"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -387,6 +406,7 @@ onUnmounted(() => {
|
|||||||
:placeholder="t('dialog.reorganize.episodePartPlaceholder')"
|
:placeholder="t('dialog.reorganize.episodePartPlaceholder')"
|
||||||
:hint="t('dialog.reorganize.episodePartHint')"
|
:hint="t('dialog.reorganize.episodePartHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-file-multiple"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -397,6 +417,7 @@ onUnmounted(() => {
|
|||||||
placeholder="0"
|
placeholder="0"
|
||||||
:hint="t('dialog.reorganize.minFileSizeHint')"
|
:hint="t('dialog.reorganize.minFileSizeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-file-document-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -58,23 +58,16 @@ const filteredSites = computed(() => {
|
|||||||
<!-- Site Selection Dialog -->
|
<!-- Site Selection Dialog -->
|
||||||
<VDialog max-width="40rem" fullscreen-mobile>
|
<VDialog max-width="40rem" fullscreen-mobile>
|
||||||
<VCard class="site-dialog">
|
<VCard class="site-dialog">
|
||||||
<VCardTitle class="d-flex align-center pa-4">
|
<VCardItem>
|
||||||
<span class="text-h6 font-weight-medium">{{ t('dialog.searchSite.selectSites') }}</span>
|
<template #prepend>
|
||||||
<VSpacer />
|
<VIcon icon="mdi-web-check" />
|
||||||
<VTextField
|
</template>
|
||||||
v-model="siteFilter"
|
<VCardTitle>
|
||||||
:placeholder="t('dialog.searchSite.siteSearch')"
|
{{ t('dialog.searchSite.selectSites') }}
|
||||||
density="compact"
|
</VCardTitle>
|
||||||
variant="outlined"
|
</VCardItem>
|
||||||
hide-details
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
class="ml-4"
|
<VDivider />
|
||||||
style="max-inline-size: 200px"
|
|
||||||
prepend-inner-icon="mdi-magnify"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</VCardTitle>
|
|
||||||
<VDivider class="search-divider" />
|
|
||||||
|
|
||||||
<VCardText style="max-block-size: 420px" class="overflow-y-auto px-4 py-4">
|
<VCardText style="max-block-size: 420px" class="overflow-y-auto px-4 py-4">
|
||||||
<!-- 站点列表 -->
|
<!-- 站点列表 -->
|
||||||
<div v-if="filteredSites.length > 0">
|
<div v-if="filteredSites.length > 0">
|
||||||
@@ -164,9 +157,6 @@ const filteredSites = computed(() => {
|
|||||||
</VCardText>
|
</VCardText>
|
||||||
|
|
||||||
<VCardActions class="pt-3">
|
<VCardActions class="pt-3">
|
||||||
<VBtn color="secondary" @click="emit('close')" class="mr-2 d-flex align-center justify-center">
|
|
||||||
{{ t('dialog.searchSite.cancel') }}
|
|
||||||
</VBtn>
|
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
<VBtn
|
<VBtn
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -148,11 +148,14 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog scrollable :close-on-back="false" eager max-width="45rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog scrollable :close-on-back="false" eager max-width="45rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard
|
<VCard>
|
||||||
:title="`${props.oper === 'add' ? t('site.actions.add') : t('site.actions.edit')}${t('site.title')}${
|
<VCardItem :class="props.oper === 'add' ? 'py-3' : 'py-2'">
|
||||||
props.oper !== 'add' ? ` - ${siteForm.name}` : ''
|
<template #prepend>
|
||||||
}`"
|
<VIcon :icon="oper == 'add' ? 'mdi-web-plus' : 'mdi-web'" class="me-2" />
|
||||||
>
|
</template>
|
||||||
|
<VCardTitle>{{ `${props.oper === 'add' ? t('site.actions.add') : t('site.actions.edit')}` }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ siteForm.name }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
@@ -165,6 +168,7 @@ onMounted(async () => {
|
|||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
:hint="t('site.hints.url')"
|
:hint="t('site.hints.url')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6" md="3">
|
<VCol cols="6" md="3">
|
||||||
@@ -175,6 +179,7 @@ onMounted(async () => {
|
|||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
:hint="t('site.hints.priority')"
|
:hint="t('site.hints.priority')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-priority-high"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6" md="3">
|
<VCol cols="6" md="3">
|
||||||
@@ -184,6 +189,7 @@ onMounted(async () => {
|
|||||||
:label="t('site.fields.status')"
|
:label="t('site.fields.status')"
|
||||||
:hint="t('site.hints.status')"
|
:hint="t('site.hints.status')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-toggle-switch"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -194,6 +200,7 @@ onMounted(async () => {
|
|||||||
:label="t('site.fields.rss')"
|
:label="t('site.fields.rss')"
|
||||||
:hint="t('site.hints.rss')"
|
:hint="t('site.hints.rss')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-rss"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="3">
|
<VCol cols="12" md="3">
|
||||||
@@ -202,6 +209,7 @@ onMounted(async () => {
|
|||||||
:label="t('site.fields.timeout')"
|
:label="t('site.fields.timeout')"
|
||||||
:hint="t('site.hints.timeout')"
|
:hint="t('site.hints.timeout')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-timer"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6" md="3">
|
<VCol cols="6" md="3">
|
||||||
@@ -211,6 +219,7 @@ onMounted(async () => {
|
|||||||
:items="downloaderOptions"
|
:items="downloaderOptions"
|
||||||
:hint="t('site.hints.downloader')"
|
:hint="t('site.hints.downloader')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-download"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -237,6 +246,7 @@ onMounted(async () => {
|
|||||||
:label="t('site.fields.cookie')"
|
:label="t('site.fields.cookie')"
|
||||||
:hint="t('site.hints.cookie')"
|
:hint="t('site.hints.cookie')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-cookie"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -245,6 +255,7 @@ onMounted(async () => {
|
|||||||
:label="t('site.fields.userAgent')"
|
:label="t('site.fields.userAgent')"
|
||||||
:hint="t('site.hints.userAgent')"
|
:hint="t('site.hints.userAgent')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-web-box"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -257,6 +268,7 @@ onMounted(async () => {
|
|||||||
:label="t('site.fields.authorization')"
|
:label="t('site.fields.authorization')"
|
||||||
:hint="t('site.hints.authorization')"
|
:hint="t('site.hints.authorization')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -265,6 +277,7 @@ onMounted(async () => {
|
|||||||
:label="t('site.fields.apiKey')"
|
:label="t('site.fields.apiKey')"
|
||||||
:hint="t('site.hints.apiKey')"
|
:hint="t('site.hints.apiKey')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-api"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -283,6 +296,7 @@ onMounted(async () => {
|
|||||||
:rules="[numberValidator]"
|
:rules="[numberValidator]"
|
||||||
:hint="t('site.hints.limitInterval')"
|
:hint="t('site.hints.limitInterval')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-clock-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4">
|
<VCol cols="12" md="4">
|
||||||
@@ -292,6 +306,7 @@ onMounted(async () => {
|
|||||||
:rules="[numberValidator]"
|
:rules="[numberValidator]"
|
||||||
:hint="t('site.hints.limitCount')"
|
:hint="t('site.hints.limitCount')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-counter"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4">
|
<VCol cols="12" md="4">
|
||||||
@@ -301,6 +316,7 @@ onMounted(async () => {
|
|||||||
:rules="[numberValidator]"
|
:rules="[numberValidator]"
|
||||||
:hint="t('site.hints.limitSeconds')"
|
:hint="t('site.hints.limitSeconds')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-timer-sand"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ onMounted(() => {
|
|||||||
density="compact"
|
density="compact"
|
||||||
:label="t('dialog.siteResource.searchKeyword')"
|
:label="t('dialog.siteResource.searchKeyword')"
|
||||||
clearable
|
clearable
|
||||||
|
prepend-inner-icon="mdi-magnify"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6" md="5">
|
<VCol cols="6" md="5">
|
||||||
@@ -165,10 +166,13 @@ onMounted(() => {
|
|||||||
:label="t('dialog.siteResource.resourceCategory')"
|
:label="t('dialog.siteResource.resourceCategory')"
|
||||||
multiple
|
multiple
|
||||||
clearable
|
clearable
|
||||||
|
prepend-inner-icon="mdi-folder"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="2" class="text-center">
|
<VCol cols="12" md="2" class="text-center">
|
||||||
<VBtn block prepend-icon="mdi-magnify" @click="getResourceList">{{ t('dialog.siteResource.search') }}</VBtn>
|
<VBtn variant="tonal" block prepend-icon="mdi-magnify" @click="getResourceList">
|
||||||
|
{{ t('dialog.siteResource.search') }}
|
||||||
|
</VBtn>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -281,18 +281,24 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard
|
<VCard>
|
||||||
:title="
|
<VCardItem class="py-2">
|
||||||
props.default
|
<template #prepend>
|
||||||
? t('dialog.subscribeEdit.titleDefault')
|
<VIcon icon="mdi-clipboard-list-outline" class="me-2" />
|
||||||
: t('dialog.subscribeEdit.titleEditFormat', {
|
</template>
|
||||||
name: subscribeForm.name,
|
<VCardTitle>
|
||||||
season: subscribeForm.season
|
{{ props.default ? t('dialog.subscribeEdit.titleDefault') : t('dialog.subscribeEdit.titleEdit') }}
|
||||||
? t('dialog.subscribeEdit.seasonFormat', { number: subscribeForm.season })
|
</VCardTitle>
|
||||||
: '',
|
<VCardSubtitle v-if="!props.default">
|
||||||
})
|
{{ subscribeForm.name }}
|
||||||
"
|
<span v-if="subscribeForm.season">
|
||||||
>
|
{{ t('dialog.subscribeEdit.seasonFormat', { number: subscribeForm.season }) }}
|
||||||
|
</span>
|
||||||
|
</VCardSubtitle>
|
||||||
|
<VCardSubtitle v-else>
|
||||||
|
{{ props.type }}
|
||||||
|
</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
@@ -314,6 +320,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.searchKeyword')"
|
:label="t('dialog.subscribeEdit.searchKeyword')"
|
||||||
:hint="t('dialog.subscribeEdit.searchKeywordHint')"
|
:hint="t('dialog.subscribeEdit.searchKeywordHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-magnify"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol v-if="subscribeForm.type === '电视剧'" cols="12" md="4">
|
<VCol v-if="subscribeForm.type === '电视剧'" cols="12" md="4">
|
||||||
@@ -323,6 +330,7 @@ onMounted(() => {
|
|||||||
:rules="[numberValidator]"
|
:rules="[numberValidator]"
|
||||||
:hint="t('dialog.subscribeEdit.totalEpisodeHint')"
|
:hint="t('dialog.subscribeEdit.totalEpisodeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-playlist-play"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol v-if="subscribeForm.type === '电视剧'" cols="12" md="4">
|
<VCol v-if="subscribeForm.type === '电视剧'" cols="12" md="4">
|
||||||
@@ -332,6 +340,7 @@ onMounted(() => {
|
|||||||
:rules="[numberValidator]"
|
:rules="[numberValidator]"
|
||||||
:hint="t('dialog.subscribeEdit.startEpisodeHint')"
|
:hint="t('dialog.subscribeEdit.startEpisodeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-play-circle-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -343,6 +352,7 @@ onMounted(() => {
|
|||||||
:items="qualityOptions"
|
:items="qualityOptions"
|
||||||
:hint="t('dialog.subscribeEdit.qualityHint')"
|
:hint="t('dialog.subscribeEdit.qualityHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-quality-high"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4">
|
<VCol cols="12" md="4">
|
||||||
@@ -352,6 +362,7 @@ onMounted(() => {
|
|||||||
:items="resolutionOptions"
|
:items="resolutionOptions"
|
||||||
:hint="t('dialog.subscribeEdit.resolutionHint')"
|
:hint="t('dialog.subscribeEdit.resolutionHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-monitor"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4">
|
<VCol cols="12" md="4">
|
||||||
@@ -361,6 +372,7 @@ onMounted(() => {
|
|||||||
:items="effectOptions"
|
:items="effectOptions"
|
||||||
:hint="t('dialog.subscribeEdit.effectHint')"
|
:hint="t('dialog.subscribeEdit.effectHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-auto-fix"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -375,6 +387,7 @@ onMounted(() => {
|
|||||||
clearable
|
clearable
|
||||||
:hint="t('dialog.subscribeEdit.subscribeSitesHint')"
|
:hint="t('dialog.subscribeEdit.subscribeSitesHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -386,6 +399,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.downloader')"
|
:label="t('dialog.subscribeEdit.downloader')"
|
||||||
:hint="t('dialog.subscribeEdit.downloaderHint')"
|
:hint="t('dialog.subscribeEdit.downloaderHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-download"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -395,6 +409,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.savePath')"
|
:label="t('dialog.subscribeEdit.savePath')"
|
||||||
:hint="t('dialog.subscribeEdit.savePathHint')"
|
:hint="t('dialog.subscribeEdit.savePathHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-folder"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -435,6 +450,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.include')"
|
:label="t('dialog.subscribeEdit.include')"
|
||||||
:hint="t('dialog.subscribeEdit.includeHint')"
|
:hint="t('dialog.subscribeEdit.includeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-plus-circle-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -443,6 +459,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.exclude')"
|
:label="t('dialog.subscribeEdit.exclude')"
|
||||||
:hint="t('dialog.subscribeEdit.excludeHint')"
|
:hint="t('dialog.subscribeEdit.excludeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-minus-circle-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -457,6 +474,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.filterGroups')"
|
:label="t('dialog.subscribeEdit.filterGroups')"
|
||||||
:hint="t('dialog.subscribeEdit.filterGroupsHint')"
|
:hint="t('dialog.subscribeEdit.filterGroupsHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-filter"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol v-if="!props.default && subscribeForm.type === '电视剧'" cols="12" md="6">
|
<VCol v-if="!props.default && subscribeForm.type === '电视剧'" cols="12" md="6">
|
||||||
@@ -467,6 +485,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.episodeGroup')"
|
:label="t('dialog.subscribeEdit.episodeGroup')"
|
||||||
:hint="t('dialog.subscribeEdit.episodeGroupHint')"
|
:hint="t('dialog.subscribeEdit.episodeGroupHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-view-list"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol v-if="!props.default && subscribeForm.type === '电视剧'" cols="12" md="6">
|
<VCol v-if="!props.default && subscribeForm.type === '电视剧'" cols="12" md="6">
|
||||||
@@ -476,6 +495,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.season')"
|
:label="t('dialog.subscribeEdit.season')"
|
||||||
:hint="t('dialog.subscribeEdit.seasonHint')"
|
:hint="t('dialog.subscribeEdit.seasonHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-calendar"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" v-if="!props.default">
|
<VCol cols="12" v-if="!props.default">
|
||||||
@@ -484,6 +504,7 @@ onMounted(() => {
|
|||||||
:label="t('dialog.subscribeEdit.mediaCategory')"
|
:label="t('dialog.subscribeEdit.mediaCategory')"
|
||||||
:hint="t('dialog.subscribeEdit.mediaCategoryHint')"
|
:hint="t('dialog.subscribeEdit.mediaCategoryHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-tag"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -495,6 +516,7 @@ onMounted(() => {
|
|||||||
:hint="t('dialog.subscribeEdit.customWordsHint')"
|
:hint="t('dialog.subscribeEdit.customWordsHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:placeholder="t('dialog.subscribeEdit.customWordsPlaceholder')"
|
:placeholder="t('dialog.subscribeEdit.customWordsPlaceholder')"
|
||||||
|
prepend-inner-icon="mdi-text"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -56,11 +56,18 @@ const $toast = useToast()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog scrollable max-width="30rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog scrollable max-width="30rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard
|
<VCard>
|
||||||
:title="`${t('dialog.subscribeShare.shareSubscription')} - ${props.sub?.name} ${
|
<VCardItem class="py-2">
|
||||||
props.sub?.season ? t('dialog.subscribeShare.season', { number: props.sub?.season }) : ''
|
<template #prepend>
|
||||||
}`"
|
<VIcon icon="mdi-share-outline" class="me-2" />
|
||||||
>
|
</template>
|
||||||
|
<VCardTitle>{{ t('dialog.subscribeShare.shareSubscription') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>
|
||||||
|
{{ props.sub?.name }}
|
||||||
|
{{ props.sub?.season ? t('dialog.subscribeShare.season', { number: props.sub?.season }) : '' }}
|
||||||
|
</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VForm @submit.prevent="() => {}" class="pt-2">
|
<VForm @submit.prevent="() => {}" class="pt-2">
|
||||||
@@ -72,6 +79,7 @@ const $toast = useToast()
|
|||||||
:label="t('dialog.subscribeShare.title')"
|
:label="t('dialog.subscribeShare.title')"
|
||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-format-title"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -81,6 +89,7 @@ const $toast = useToast()
|
|||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
:hint="t('dialog.subscribeShare.descriptionHint')"
|
:hint="t('dialog.subscribeShare.descriptionHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-comment-text-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -90,6 +99,7 @@ const $toast = useToast()
|
|||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
:hint="t('dialog.subscribeShare.shareUserHint')"
|
:hint="t('dialog.subscribeShare.shareUserHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -119,6 +119,9 @@ onUnmounted(() => {
|
|||||||
<VCard>
|
<VCard>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-qrcode" class="me-2" />
|
||||||
|
</template>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
{{ t('dialog.u115Auth.loginTitle') }}
|
{{ t('dialog.u115Auth.loginTitle') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
|
|||||||
@@ -291,11 +291,14 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog scrollable max-width="40rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog scrollable max-width="40rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard
|
<VCard>
|
||||||
:title="`${props.oper === 'add' ? t('dialog.userAddEdit.add') : t('dialog.userAddEdit.edit')}${
|
<VCardItem :class="props.oper === 'add' ? 'py-3' : 'py-2'">
|
||||||
props.oper !== 'add' ? ` - ${userName}` : ''
|
<template #prepend>
|
||||||
}`"
|
<VIcon icon="mdi-account" class="me-2" />
|
||||||
>
|
</template>
|
||||||
|
<VCardTitle>{{ props.oper === 'add' ? t('dialog.userAddEdit.add') : t('dialog.userAddEdit.edit') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ userName }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
@@ -350,6 +353,7 @@ onMounted(() => {
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
:readonly="props.oper !== 'add'"
|
:readonly="props.oper !== 'add'"
|
||||||
:label="t('dialog.userAddEdit.username')"
|
:label="t('dialog.userAddEdit.username')"
|
||||||
|
prepend-inner-icon="mdi-account"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -359,6 +363,7 @@ onMounted(() => {
|
|||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.email')"
|
:label="t('dialog.userAddEdit.email')"
|
||||||
type="email"
|
type="email"
|
||||||
|
prepend-inner-icon="mdi-email"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -370,6 +375,7 @@ onMounted(() => {
|
|||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.password')"
|
:label="t('dialog.userAddEdit.password')"
|
||||||
autocomplete=""
|
autocomplete=""
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -382,6 +388,7 @@ onMounted(() => {
|
|||||||
:append-inner-icon="isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
:append-inner-icon="isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.confirmPassword')"
|
:label="t('dialog.userAddEdit.confirmPassword')"
|
||||||
|
prepend-inner-icon="mdi-lock-check"
|
||||||
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
|
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -392,6 +399,7 @@ onMounted(() => {
|
|||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.nickname')"
|
:label="t('dialog.userAddEdit.nickname')"
|
||||||
placeholder="显示昵称,优先于用户名显示"
|
placeholder="显示昵称,优先于用户名显示"
|
||||||
|
prepend-inner-icon="mdi-card-account-details"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6" v-if="canControl">
|
<VCol cols="12" md="6" v-if="canControl">
|
||||||
@@ -402,6 +410,7 @@ onMounted(() => {
|
|||||||
item-value="value"
|
item-value="value"
|
||||||
:label="t('dialog.userAddEdit.status')"
|
:label="t('dialog.userAddEdit.status')"
|
||||||
dense
|
dense
|
||||||
|
prepend-inner-icon="mdi-toggle-switch"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -415,6 +424,7 @@ onMounted(() => {
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.wechat')"
|
:label="t('dialog.userAddEdit.wechat')"
|
||||||
|
prepend-inner-icon="mdi-wechat"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -423,6 +433,7 @@ onMounted(() => {
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.telegram')"
|
:label="t('dialog.userAddEdit.telegram')"
|
||||||
|
prepend-inner-icon="mdi-send"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -431,6 +442,7 @@ onMounted(() => {
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.slack')"
|
:label="t('dialog.userAddEdit.slack')"
|
||||||
|
prepend-inner-icon="mdi-slack"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -439,6 +451,7 @@ onMounted(() => {
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.vocechat')"
|
:label="t('dialog.userAddEdit.vocechat')"
|
||||||
|
prepend-inner-icon="mdi-chat"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -447,10 +460,17 @@ onMounted(() => {
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('dialog.userAddEdit.synologyChat')"
|
:label="t('dialog.userAddEdit.synologyChat')"
|
||||||
|
prepend-inner-icon="mdi-message"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField v-model="userForm.settings.douban_userid" density="comfortable" clearable label="豆瓣用户" />
|
<VTextField
|
||||||
|
v-model="userForm.settings.douban_userid"
|
||||||
|
density="comfortable"
|
||||||
|
clearable
|
||||||
|
label="豆瓣用户"
|
||||||
|
prepend-inner-icon="mdi-movie"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VForm>
|
</VForm>
|
||||||
|
|||||||
@@ -138,8 +138,15 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog width="40rem" scrollable :fullscreen="!display.mdAndUp.value">
|
<VDialog width="40rem" scrollable :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard :title="t('dialog.userAuth.title')">
|
<VCard>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VCardItem>
|
||||||
|
<VCardTitle>
|
||||||
|
<VIcon icon="mdi-user-check" class="me-2" />
|
||||||
|
{{ t('dialog.userAuth.title') }}
|
||||||
|
</VCardTitle>
|
||||||
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
|
</VCardItem>
|
||||||
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -150,6 +157,7 @@ onMounted(async () => {
|
|||||||
item-title="name"
|
item-title="name"
|
||||||
:label="t('dialog.userAuth.selectSite')"
|
:label="t('dialog.userAuth.selectSite')"
|
||||||
item-props
|
item-props
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
>
|
>
|
||||||
</VSelect>
|
</VSelect>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|||||||
@@ -86,7 +86,13 @@ async function editWorkflow() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog scrollable :close-on-back="false" eager max-width="30rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog scrollable :close-on-back="false" eager max-width="30rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard :title="title">
|
<VCard>
|
||||||
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-clock-outline" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ title }}</VCardTitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
@@ -99,6 +105,7 @@ async function editWorkflow() {
|
|||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:hint="t('dialog.workflowAddEdit.namePlaceholder')"
|
:hint="t('dialog.workflowAddEdit.namePlaceholder')"
|
||||||
|
prepend-inner-icon="mdi-workflow"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -109,6 +116,7 @@ async function editWorkflow() {
|
|||||||
placeholder="5位cron表达式"
|
placeholder="5位cron表达式"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:hint="t('dialog.workflowAddEdit.cronExprDesc')"
|
:hint="t('dialog.workflowAddEdit.cronExprDesc')"
|
||||||
|
prepend-inner-icon="mdi-clock-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -116,6 +124,7 @@ async function editWorkflow() {
|
|||||||
v-model="workflowForm.description"
|
v-model="workflowForm.description"
|
||||||
:label="t('dialog.workflowAddEdit.desc')"
|
:label="t('dialog.workflowAddEdit.desc')"
|
||||||
:placeholder="t('dialog.workflowAddEdit.descPlaceholder')"
|
:placeholder="t('dialog.workflowAddEdit.descPlaceholder')"
|
||||||
|
prepend-inner-icon="mdi-text-box-outline"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -696,13 +696,24 @@ onMounted(() => {
|
|||||||
</VCard>
|
</VCard>
|
||||||
<!-- 重命名弹窗 -->
|
<!-- 重命名弹窗 -->
|
||||||
<VDialog v-if="renamePopper" v-model="renamePopper" max-width="35rem">
|
<VDialog v-if="renamePopper" v-model="renamePopper" max-width="35rem">
|
||||||
<VCard :title="t('file.rename')">
|
<VCard>
|
||||||
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-pencil" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ t('file.rename') }}</VCardTitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn @click="renamePopper = false" />
|
<VDialogCloseBtn @click="renamePopper = false" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VTextField v-model="newName" :label="t('file.newName')" :loading="renameLoading" />
|
<VTextField
|
||||||
|
v-model="newName"
|
||||||
|
:label="t('file.newName')"
|
||||||
|
:loading="renameLoading"
|
||||||
|
prepend-inner-icon="mdi-format-text"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" v-if="currentItem && currentItem.type == 'dir'">
|
<VCol cols="12" v-if="currentItem && currentItem.type == 'dir'">
|
||||||
<VSwitch v-model="renameAll" :label="t('file.includeSubfolders')" />
|
<VSwitch v-model="renameAll" :label="t('file.includeSubfolders')" />
|
||||||
|
|||||||
@@ -165,17 +165,24 @@ const sortIcon = computed(() => {
|
|||||||
<IconBtn v-if="pathSegments.length > 0" @click="goUp">
|
<IconBtn v-if="pathSegments.length > 0" @click="goUp">
|
||||||
<VIcon icon="mdi-arrow-up-bold-outline" />
|
<VIcon icon="mdi-arrow-up-bold-outline" />
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
|
<!-- 新建文件夹 -->
|
||||||
<VDialog v-model="newFolderPopper" max-width="35rem">
|
<VDialog v-model="newFolderPopper" max-width="35rem">
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props }">
|
||||||
<IconBtn>
|
<IconBtn>
|
||||||
<VIcon v-bind="props" icon="mdi-folder-plus-outline" />
|
<VIcon v-bind="props" icon="mdi-folder-plus-outline" />
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
</template>
|
</template>
|
||||||
<VCard :title="t('file.newFolder')">
|
<VCard>
|
||||||
|
<VCardItem>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-folder-plus-outline" class="me-2" />
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ t('file.newFolder') }}</VCardTitle>
|
||||||
|
</VCardItem>
|
||||||
<VDialogCloseBtn @click="newFolderPopper = false" />
|
<VDialogCloseBtn @click="newFolderPopper = false" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VTextField v-model="newFolderName" :label="t('common.name')" />
|
<VTextField v-model="newFolderName" :label="t('common.name')" prepend-inner-icon="mdi-format-text" />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardActions>
|
<VCardActions>
|
||||||
<div class="flex-grow-1" />
|
<div class="flex-grow-1" />
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ onMounted(() => {
|
|||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<LoggingView />
|
<LoggingView logfile="moviepilot.log" />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ export default {
|
|||||||
unsubscribe: 'Unsubscribe',
|
unsubscribe: 'Unsubscribe',
|
||||||
media: 'Media',
|
media: 'Media',
|
||||||
unknown: 'Unknown',
|
unknown: 'Unknown',
|
||||||
|
notice: 'Notice',
|
||||||
|
itemsPerPage: 'Items per page',
|
||||||
|
pageText: '{0}-{1} of {2}',
|
||||||
|
noDataText: 'No data',
|
||||||
|
loadingText: 'Loading...',
|
||||||
},
|
},
|
||||||
mediaType: {
|
mediaType: {
|
||||||
movie: 'Movie',
|
movie: 'Movie',
|
||||||
@@ -201,6 +206,10 @@ export default {
|
|||||||
title: 'Services',
|
title: 'Services',
|
||||||
description: 'Scheduled jobs',
|
description: 'Scheduled jobs',
|
||||||
},
|
},
|
||||||
|
cache: {
|
||||||
|
title: 'Cache',
|
||||||
|
description: 'Torrent cache, media recognition data cache, image file cache management',
|
||||||
|
},
|
||||||
notification: {
|
notification: {
|
||||||
title: 'Notifications',
|
title: 'Notifications',
|
||||||
description: 'Notification channels (WeChat, Telegram, Slack, SynologyChat, VoceChat, WebPush), message scope',
|
description: 'Notification channels (WeChat, Telegram, Slack, SynologyChat, VoceChat, WebPush), message scope',
|
||||||
@@ -852,8 +861,8 @@ export default {
|
|||||||
browserSimulation: 'Use browser simulation for authentic site access',
|
browserSimulation: 'Use browser simulation for authentic site access',
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
add: 'Add',
|
add: 'Add Site',
|
||||||
edit: 'Edit',
|
edit: 'Edit Site',
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
addSuccess: 'Site added successfully',
|
addSuccess: 'Site added successfully',
|
||||||
@@ -1095,10 +1104,16 @@ export default {
|
|||||||
securityImageDomainsHint: 'Allowed image domains whitelist for caching, used to control trusted image sources',
|
securityImageDomainsHint: 'Allowed image domains whitelist for caching, used to control trusted image sources',
|
||||||
noSecurityImageDomains: 'No security domains',
|
noSecurityImageDomains: 'No security domains',
|
||||||
securityImageDomainAdd: 'Add domain, e.g.: image.tmdb.org',
|
securityImageDomainAdd: 'Add domain, e.g.: image.tmdb.org',
|
||||||
|
proxyHost: 'Proxy Server',
|
||||||
|
proxyHostHint: 'Set proxy server address, support: http(s), socks5, socks5h, etc.',
|
||||||
|
moviePilotAutoUpdate: 'Auto Update MoviePilot',
|
||||||
|
moviePilotAutoUpdateHint: 'Automatically update MoviePilot to the latest release version when restarting',
|
||||||
|
autoUpdateResource: 'Auto Update Resource',
|
||||||
|
autoUpdateResourceHint: 'Automatically detect and update site resource package when restarting',
|
||||||
},
|
},
|
||||||
site: {
|
site: {
|
||||||
siteSync: 'Site Synchronization',
|
siteSync: 'Site Synchronization',
|
||||||
siteSyncDesc: 'Quickly sync site data from CookieCloud.',
|
siteSyncDesc: 'Quickly sync site data from CookieCloud',
|
||||||
enableLocalCookieCloud: 'Enable Local CookieCloud Server',
|
enableLocalCookieCloud: 'Enable Local CookieCloud Server',
|
||||||
enableLocalCookieCloudHint:
|
enableLocalCookieCloudHint:
|
||||||
'Use built-in CookieCloud service to sync site data, service address: http://localhost:3000/cookiecloud',
|
'Use built-in CookieCloud service to sync site data, service address: http://localhost:3000/cookiecloud',
|
||||||
@@ -1144,7 +1159,7 @@ export default {
|
|||||||
},
|
},
|
||||||
notification: {
|
notification: {
|
||||||
channels: 'Notification Channels',
|
channels: 'Notification Channels',
|
||||||
channelsDesc: 'Set message sending channel parameters.',
|
channelsDesc: 'Set message sending channel parameters',
|
||||||
organizeSuccess: 'Media Import',
|
organizeSuccess: 'Media Import',
|
||||||
downloadAdded: 'Download Added',
|
downloadAdded: 'Download Added',
|
||||||
subscribeAdded: 'Subscribe Added',
|
subscribeAdded: 'Subscribe Added',
|
||||||
@@ -1191,7 +1206,7 @@ export default {
|
|||||||
},
|
},
|
||||||
words: {
|
words: {
|
||||||
customIdentifiers: 'Custom Identifiers',
|
customIdentifiers: 'Custom Identifiers',
|
||||||
identifiersDesc: 'Add rules to preprocess torrent names or file names to correct identification.',
|
identifiersDesc: 'Add rules to preprocess torrent names or file names to correct identification',
|
||||||
identifiersPlaceholder: 'Support regular expressions, special characters need \\ escape, one line for each rule',
|
identifiersPlaceholder: 'Support regular expressions, special characters need \\ escape, one line for each rule',
|
||||||
identifiersHint: 'Support regular expressions, special characters need \\ escape, one line for each rule',
|
identifiersHint: 'Support regular expressions, special characters need \\ escape, one line for each rule',
|
||||||
formatTitle: 'Supported configuration formats (mind the spaces):',
|
formatTitle: 'Supported configuration formats (mind the spaces):',
|
||||||
@@ -1231,7 +1246,7 @@ export default {
|
|||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
basicSettings: 'Basic Settings',
|
basicSettings: 'Basic Settings',
|
||||||
basicSettingsDesc: 'Set data sources, rule groups and other basic information.',
|
basicSettingsDesc: 'Set data sources, rule groups and other basic information',
|
||||||
recognizeSource: 'Recognition Data Source',
|
recognizeSource: 'Recognition Data Source',
|
||||||
recognizeSourceDesc:
|
recognizeSourceDesc:
|
||||||
'Default is TMDB. Douban is usually more friendly for Chinese works, but some foreign works have incomplete information.',
|
'Default is TMDB. Douban is usually more friendly for Chinese works, but some foreign works have incomplete information.',
|
||||||
@@ -1332,8 +1347,7 @@ export default {
|
|||||||
},
|
},
|
||||||
scheduler: {
|
scheduler: {
|
||||||
title: 'Scheduled Jobs',
|
title: 'Scheduled Jobs',
|
||||||
subtitle:
|
subtitle: 'Includes built-in system services and plugin services',
|
||||||
"Includes built-in system services and plugin services. Manual execution will not affect the job's normal schedule.",
|
|
||||||
provider: 'Provider',
|
provider: 'Provider',
|
||||||
taskName: 'Task Name',
|
taskName: 'Task Name',
|
||||||
taskStatus: 'Task Status',
|
taskStatus: 'Task Status',
|
||||||
@@ -1380,6 +1394,55 @@ export default {
|
|||||||
settingsSaveSuccess: 'Subscription basic settings saved successfully',
|
settingsSaveSuccess: 'Subscription basic settings saved successfully',
|
||||||
settingsSaveFailed: 'Failed to save subscription basic settings!',
|
settingsSaveFailed: 'Failed to save subscription basic settings!',
|
||||||
},
|
},
|
||||||
|
cache: {
|
||||||
|
title: 'Cache Management',
|
||||||
|
subtitle: 'Manage torrent cache data',
|
||||||
|
filterByTitle: 'Filter by Title',
|
||||||
|
filterBySite: 'Filter by Site',
|
||||||
|
selectSite: 'Select Site',
|
||||||
|
refresh: 'Refresh Cache',
|
||||||
|
deleteSelected: 'Delete Selected',
|
||||||
|
clearAll: 'Clear All Cache',
|
||||||
|
refreshSuccess: 'Cache refresh completed',
|
||||||
|
refreshFailed: 'Failed to refresh cache',
|
||||||
|
clearSuccess: 'Cache clear completed',
|
||||||
|
clearFailed: 'Failed to clear cache',
|
||||||
|
deleteSuccess: 'Cache item deleted successfully',
|
||||||
|
deleteFailed: 'Failed to delete cache item',
|
||||||
|
deleteSelectedSuccess: 'Successfully deleted {count} cache items',
|
||||||
|
deleteSelectedFailed: 'Failed to delete cache items',
|
||||||
|
loadFailed: 'Failed to load cache data',
|
||||||
|
selectDeleteWarning: 'Please select cache items to delete',
|
||||||
|
reidentify: 'Re-identify',
|
||||||
|
reidentifySuccess: 'Re-identification completed',
|
||||||
|
reidentifyFailed: 'Re-identification failed',
|
||||||
|
poster: 'Poster',
|
||||||
|
torrentTitle: 'Title',
|
||||||
|
site: 'Site',
|
||||||
|
size: 'Size',
|
||||||
|
publishTime: 'Publish Time',
|
||||||
|
recognitionResult: 'Recognition Result',
|
||||||
|
actions: 'Actions',
|
||||||
|
unrecognized: 'Unrecognized',
|
||||||
|
noData: 'No cache data',
|
||||||
|
noDataHint: 'Click "Refresh Cache" button to get the latest torrent cache',
|
||||||
|
reidentifyDialog: {
|
||||||
|
title: 'Re-identify',
|
||||||
|
torrentInfo: 'Torrent Info',
|
||||||
|
tmdbId: 'TMDB ID',
|
||||||
|
tmdbIdHint: 'Optional, manually specify TMDB ID for recognition',
|
||||||
|
doubanId: 'Douban ID',
|
||||||
|
doubanIdHint: 'Optional, manually specify Douban ID for recognition',
|
||||||
|
autoHint: 'If no ID is specified, the torrent will be automatically re-identified',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
confirm: 'Re-identify',
|
||||||
|
},
|
||||||
|
mediaType: {
|
||||||
|
movie: 'Movie',
|
||||||
|
tv: 'TV Show',
|
||||||
|
},
|
||||||
|
clearConfirm: 'Are you sure you want to clear all cache?',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
progress: {
|
progress: {
|
||||||
@@ -1703,8 +1766,8 @@ export default {
|
|||||||
previous: 'Previous',
|
previous: 'Previous',
|
||||||
confirm: 'Confirm',
|
confirm: 'Confirm',
|
||||||
manualTitle: 'Manual Organization',
|
manualTitle: 'Manual Organization',
|
||||||
multipleItemsTitle: 'Organize - {count} Items',
|
multipleItemsTitle: '{count} Items',
|
||||||
singleItemTitle: 'Organize - {path}',
|
singleItemTitle: '{path}',
|
||||||
targetStorage: 'Target Storage',
|
targetStorage: 'Target Storage',
|
||||||
targetStorageHint: 'Organization target storage',
|
targetStorageHint: 'Organization target storage',
|
||||||
transferType: 'Organization Method',
|
transferType: 'Organization Method',
|
||||||
@@ -1753,7 +1816,7 @@ export default {
|
|||||||
},
|
},
|
||||||
subscribeEdit: {
|
subscribeEdit: {
|
||||||
titleDefault: 'Default Subscription Rules',
|
titleDefault: 'Default Subscription Rules',
|
||||||
titleEditFormat: 'Edit Subscription - {name} {season}',
|
titleEdit: 'Edit Subscription',
|
||||||
seasonFormat: 'Season {number}',
|
seasonFormat: 'Season {number}',
|
||||||
tabs: {
|
tabs: {
|
||||||
basic: 'Basic',
|
basic: 'Basic',
|
||||||
@@ -2013,6 +2076,40 @@ export default {
|
|||||||
folderName: 'Folder Name',
|
folderName: 'Folder Name',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
create: 'Create',
|
create: 'Create',
|
||||||
|
clone: 'Clone',
|
||||||
|
cloneTitle: 'Create Plugin Clone',
|
||||||
|
cloneSubtitle: 'Create an independent clone instance for {name}',
|
||||||
|
cloneFeature: 'Plugin Clone Feature',
|
||||||
|
cloneDescription:
|
||||||
|
'Create an independent copy of the plugin with separate configuration and data, suitable for multi-account, testing environments, etc.',
|
||||||
|
suffix: 'Clone Suffix',
|
||||||
|
suffixPlaceholder: 'e.g.: Test, Backup, Site1',
|
||||||
|
suffixHint: 'Unique identifier to distinguish clones, only letters and numbers allowed',
|
||||||
|
suffixRequired: 'Clone suffix cannot be empty',
|
||||||
|
suffixFormatError: 'Only letters and numbers allowed',
|
||||||
|
suffixLengthError: 'Length cannot exceed 20 characters',
|
||||||
|
cloneName: 'Clone Name',
|
||||||
|
cloneNamePlaceholder: 'e.g.: Auto Backup Test Version',
|
||||||
|
cloneNameHint: 'Display name for the clone plugin (optional)',
|
||||||
|
cloneDefaultName: '{name} Clone',
|
||||||
|
cloneDescriptionLabel: 'Clone Description',
|
||||||
|
cloneDescriptionPlaceholder: 'Describe the purpose and features of this clone...',
|
||||||
|
cloneDescriptionHint: 'Detailed description of the clone plugin purpose (optional)',
|
||||||
|
cloneDefaultDescription: '{description} (Clone Version)',
|
||||||
|
cloneVersion: 'Version',
|
||||||
|
cloneVersionPlaceholder: 'e.g.: 1.0, 2.1.0',
|
||||||
|
cloneVersionHint: 'Custom version number for the clone plugin (optional)',
|
||||||
|
cloneIcon: 'Icon URL',
|
||||||
|
cloneIconPlaceholder: 'https://example.com/icon.png',
|
||||||
|
cloneIconHint: 'Custom icon for the clone plugin (optional)',
|
||||||
|
cloneNotice:
|
||||||
|
'Clone plugins are disabled by default after creation and need to be manually configured and enabled. The clone suffix cannot be modified once set.',
|
||||||
|
createClone: 'Create Clone',
|
||||||
|
cloning: 'Creating clone for {name}...',
|
||||||
|
cloneSuccess: 'Plugin clone {name} created successfully!',
|
||||||
|
cloneFailed: 'Plugin clone creation failed: {message}',
|
||||||
|
cloneFailedGeneral: 'Plugin clone creation failed',
|
||||||
|
logTitle: 'Plugin Logging',
|
||||||
},
|
},
|
||||||
profile: {
|
profile: {
|
||||||
personalInfo: 'Personal Information',
|
personalInfo: 'Personal Information',
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ export default {
|
|||||||
unsubscribe: '取消订阅',
|
unsubscribe: '取消订阅',
|
||||||
media: '媒体',
|
media: '媒体',
|
||||||
unknown: '未知',
|
unknown: '未知',
|
||||||
|
notice: '注意',
|
||||||
|
itemsPerPage: '每页条数',
|
||||||
|
pageText: '{0}-{1} 共 {2} 条',
|
||||||
|
noDataText: '没有数据',
|
||||||
|
loadingText: '加载中...',
|
||||||
},
|
},
|
||||||
mediaType: {
|
mediaType: {
|
||||||
movie: '电影',
|
movie: '电影',
|
||||||
@@ -201,6 +206,10 @@ export default {
|
|||||||
title: '服务',
|
title: '服务',
|
||||||
description: '定时作业',
|
description: '定时作业',
|
||||||
},
|
},
|
||||||
|
cache: {
|
||||||
|
title: '缓存',
|
||||||
|
description: '种子缓存、图片文件缓存管理',
|
||||||
|
},
|
||||||
notification: {
|
notification: {
|
||||||
title: '通知',
|
title: '通知',
|
||||||
description: '通知渠道(微信、Telegram、Slack、SynologyChat、VoceChat、WebPush)、消息发送范围',
|
description: '通知渠道(微信、Telegram、Slack、SynologyChat、VoceChat、WebPush)、消息发送范围',
|
||||||
@@ -849,8 +858,8 @@ export default {
|
|||||||
browserSimulation: '使用浏览器模拟真实访问该站点',
|
browserSimulation: '使用浏览器模拟真实访问该站点',
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
add: '新增',
|
add: '新增站点',
|
||||||
edit: '编辑',
|
edit: '编辑站点',
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
addSuccess: '新增站点成功',
|
addSuccess: '新增站点成功',
|
||||||
@@ -936,7 +945,7 @@ export default {
|
|||||||
system: {
|
system: {
|
||||||
custom: '自定义',
|
custom: '自定义',
|
||||||
basicSettings: '基础设置',
|
basicSettings: '基础设置',
|
||||||
basicSettingsDesc: '设置服务器的全局功能。',
|
basicSettingsDesc: '设置服务器的全局功能',
|
||||||
appDomain: '访问域名',
|
appDomain: '访问域名',
|
||||||
appDomainHint: '用于发送通知时,添加快捷跳转地址',
|
appDomainHint: '用于发送通知时,添加快捷跳转地址',
|
||||||
wallpaper: '背景壁纸',
|
wallpaper: '背景壁纸',
|
||||||
@@ -1085,10 +1094,16 @@ export default {
|
|||||||
securityImageDomainsHint: '允许缓存的图片域名白名单,用于控制可信任的图片来源',
|
securityImageDomainsHint: '允许缓存的图片域名白名单,用于控制可信任的图片来源',
|
||||||
noSecurityImageDomains: '暂无安全域名',
|
noSecurityImageDomains: '暂无安全域名',
|
||||||
securityImageDomainAdd: '添加域名,如:image.tmdb.org',
|
securityImageDomainAdd: '添加域名,如:image.tmdb.org',
|
||||||
|
proxyHost: '代理服务器',
|
||||||
|
proxyHostHint: '设置代理服务器地址,支持:http(s)、socks5、socks5h 等协议',
|
||||||
|
moviePilotAutoUpdate: '自动更新MoviePilot',
|
||||||
|
moviePilotAutoUpdateHint: '重启时自动更新MoviePilot到最新发行版本',
|
||||||
|
autoUpdateResource: '自动更新站点资源',
|
||||||
|
autoUpdateResourceHint: '重启时自动检测和更新站点资源包',
|
||||||
},
|
},
|
||||||
site: {
|
site: {
|
||||||
siteSync: '站点同步',
|
siteSync: '站点同步',
|
||||||
siteSyncDesc: '从CookieCloud快速同步站点数据。',
|
siteSyncDesc: '从CookieCloud快速同步站点数据',
|
||||||
enableLocalCookieCloud: '启用本地CookieCloud服务器',
|
enableLocalCookieCloud: '启用本地CookieCloud服务器',
|
||||||
enableLocalCookieCloudHint: '使用内建CookieCloud服务同步站点数据,服务地址为:http://localhost:3000/cookiecloud',
|
enableLocalCookieCloudHint: '使用内建CookieCloud服务同步站点数据,服务地址为:http://localhost:3000/cookiecloud',
|
||||||
serviceAddress: '服务地址',
|
serviceAddress: '服务地址',
|
||||||
@@ -1131,7 +1146,7 @@ export default {
|
|||||||
},
|
},
|
||||||
notification: {
|
notification: {
|
||||||
channels: '通知渠道',
|
channels: '通知渠道',
|
||||||
channelsDesc: '设置消息发送渠道参数。',
|
channelsDesc: '设置消息发送渠道参数',
|
||||||
organizeSuccess: '资源入库',
|
organizeSuccess: '资源入库',
|
||||||
downloadAdded: '资源下载',
|
downloadAdded: '资源下载',
|
||||||
subscribeAdded: '添加订阅',
|
subscribeAdded: '添加订阅',
|
||||||
@@ -1178,7 +1193,7 @@ export default {
|
|||||||
},
|
},
|
||||||
words: {
|
words: {
|
||||||
customIdentifiers: '自定义识别词',
|
customIdentifiers: '自定义识别词',
|
||||||
identifiersDesc: '添加规则对种子名或者文件名进行预处理以校正识别。',
|
identifiersDesc: '添加规则对种子名或者文件名进行预处理以校正识别',
|
||||||
identifiersPlaceholder: '支持正则表达式,特殊字符需要\\转义,一行为一组',
|
identifiersPlaceholder: '支持正则表达式,特殊字符需要\\转义,一行为一组',
|
||||||
identifiersHint: '支持正则表达式,特殊字符需要\\转义,一行为一组',
|
identifiersHint: '支持正则表达式,特殊字符需要\\转义,一行为一组',
|
||||||
formatTitle: '支持的配置格式(注意空格):',
|
formatTitle: '支持的配置格式(注意空格):',
|
||||||
@@ -1214,7 +1229,7 @@ export default {
|
|||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
basicSettings: '基础设置',
|
basicSettings: '基础设置',
|
||||||
basicSettingsDesc: '设定数据源、规则组等基础信息。',
|
basicSettingsDesc: '设定数据源、规则组等基础信息',
|
||||||
recognizeSource: '识别数据源',
|
recognizeSource: '识别数据源',
|
||||||
recognizeSourceDesc: '默认使用TMDB。豆瓣识别中文作品通常更友好,但有些国外作品信息不完整。',
|
recognizeSourceDesc: '默认使用TMDB。豆瓣识别中文作品通常更友好,但有些国外作品信息不完整。',
|
||||||
themoviedb: 'TheMovieDb',
|
themoviedb: 'TheMovieDb',
|
||||||
@@ -1250,7 +1265,7 @@ export default {
|
|||||||
},
|
},
|
||||||
directory: {
|
directory: {
|
||||||
storage: '存储',
|
storage: '存储',
|
||||||
storageDesc: '设置本地或网盘存储。',
|
storageDesc: '设置本地或网盘存储',
|
||||||
directory: '目录',
|
directory: '目录',
|
||||||
mediaType: '媒体类型',
|
mediaType: '媒体类型',
|
||||||
directoryDesc: '设置媒体文件整理目录结构,按先后顺序依次匹配。',
|
directoryDesc: '设置媒体文件整理目录结构,按先后顺序依次匹配。',
|
||||||
@@ -1312,7 +1327,7 @@ export default {
|
|||||||
},
|
},
|
||||||
scheduler: {
|
scheduler: {
|
||||||
title: '定时作业',
|
title: '定时作业',
|
||||||
subtitle: '包含系统内置服务以及插件提供的服务,手动执行不会影响作业正常的时间表。',
|
subtitle: '包含系统内置服务以及插件提供的服务',
|
||||||
provider: '提供者',
|
provider: '提供者',
|
||||||
taskName: '任务名称',
|
taskName: '任务名称',
|
||||||
taskStatus: '任务状态',
|
taskStatus: '任务状态',
|
||||||
@@ -1359,6 +1374,55 @@ export default {
|
|||||||
settingsSaveSuccess: '订阅基础设置保存成功',
|
settingsSaveSuccess: '订阅基础设置保存成功',
|
||||||
settingsSaveFailed: '订阅基础设置保存失败!',
|
settingsSaveFailed: '订阅基础设置保存失败!',
|
||||||
},
|
},
|
||||||
|
cache: {
|
||||||
|
title: '缓存管理',
|
||||||
|
subtitle: '管理缓存的站点资源',
|
||||||
|
filterByTitle: '按标题筛选',
|
||||||
|
filterBySite: '按站点筛选',
|
||||||
|
selectSite: '选择站点',
|
||||||
|
refresh: '刷新缓存',
|
||||||
|
deleteSelected: '删除选中',
|
||||||
|
clearAll: '清空缓存',
|
||||||
|
refreshSuccess: '缓存刷新完成',
|
||||||
|
refreshFailed: '刷新缓存失败',
|
||||||
|
clearSuccess: '缓存清理完成',
|
||||||
|
clearFailed: '清理缓存失败',
|
||||||
|
deleteSuccess: '缓存项删除成功',
|
||||||
|
deleteFailed: '删除缓存项失败',
|
||||||
|
deleteSelectedSuccess: '成功删除 {count} 个缓存项',
|
||||||
|
deleteSelectedFailed: '删除缓存项失败',
|
||||||
|
loadFailed: '加载缓存数据失败',
|
||||||
|
selectDeleteWarning: '请选择要删除的缓存项',
|
||||||
|
reidentify: '重新识别',
|
||||||
|
reidentifySuccess: '重新识别完成',
|
||||||
|
reidentifyFailed: '重新识别失败',
|
||||||
|
poster: '海报',
|
||||||
|
torrentTitle: '标题',
|
||||||
|
site: '站点',
|
||||||
|
size: '大小',
|
||||||
|
publishTime: '发布时间',
|
||||||
|
recognitionResult: '识别结果',
|
||||||
|
actions: '操作',
|
||||||
|
unrecognized: '未识别',
|
||||||
|
noData: '暂无缓存数据',
|
||||||
|
noDataHint: '点击"刷新缓存"按钮获取最新的种子缓存',
|
||||||
|
reidentifyDialog: {
|
||||||
|
title: '重新识别',
|
||||||
|
torrentInfo: '种子信息',
|
||||||
|
tmdbId: 'TMDB ID',
|
||||||
|
tmdbIdHint: '可选,手动指定TMDB ID进行识别',
|
||||||
|
doubanId: '豆瓣 ID',
|
||||||
|
doubanIdHint: '可选,手动指定豆瓣ID进行识别',
|
||||||
|
autoHint: '如果不指定ID,将自动重新识别该种子',
|
||||||
|
cancel: '取消',
|
||||||
|
confirm: '重新识别',
|
||||||
|
},
|
||||||
|
mediaType: {
|
||||||
|
movie: '电影',
|
||||||
|
tv: '电视剧',
|
||||||
|
},
|
||||||
|
clearConfirm: '确认清空所有缓存吗?',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
progress: {
|
progress: {
|
||||||
@@ -1680,8 +1744,8 @@ export default {
|
|||||||
previous: '上一步',
|
previous: '上一步',
|
||||||
confirm: '确认',
|
confirm: '确认',
|
||||||
manualTitle: '手动整理',
|
manualTitle: '手动整理',
|
||||||
multipleItemsTitle: '整理 - 共 {count} 项',
|
multipleItemsTitle: '共 {count} 项',
|
||||||
singleItemTitle: '整理 - {path}',
|
singleItemTitle: '{path}',
|
||||||
targetStorage: '目的存储',
|
targetStorage: '目的存储',
|
||||||
targetStorageHint: '整理目的存储',
|
targetStorageHint: '整理目的存储',
|
||||||
transferType: '整理方式',
|
transferType: '整理方式',
|
||||||
@@ -1730,7 +1794,7 @@ export default {
|
|||||||
},
|
},
|
||||||
subscribeEdit: {
|
subscribeEdit: {
|
||||||
titleDefault: '默认订阅规则',
|
titleDefault: '默认订阅规则',
|
||||||
titleEditFormat: '编辑订阅 - {name} {season}',
|
titleEdit: '编辑订阅',
|
||||||
seasonFormat: '第 {number} 季',
|
seasonFormat: '第 {number} 季',
|
||||||
tabs: {
|
tabs: {
|
||||||
basic: '基础',
|
basic: '基础',
|
||||||
@@ -1989,6 +2053,38 @@ export default {
|
|||||||
folderName: '文件夹名称',
|
folderName: '文件夹名称',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
create: '创建',
|
create: '创建',
|
||||||
|
clone: '分身',
|
||||||
|
cloneTitle: '创建插件分身',
|
||||||
|
cloneSubtitle: '为 {name} 创建独立的分身实例',
|
||||||
|
cloneFeature: '插件分身功能',
|
||||||
|
cloneDescription: '创建插件的独立副本,拥有独立的配置和数据,适用于多账号、测试环境等场景',
|
||||||
|
suffix: '分身后缀',
|
||||||
|
suffixPlaceholder: '例如:Test、Backup、Site1',
|
||||||
|
suffixHint: '用于区分分身的唯一标识,只能包含英文字母和数字',
|
||||||
|
suffixRequired: '分身后缀不能为空',
|
||||||
|
suffixFormatError: '只能包含英文字母和数字',
|
||||||
|
suffixLengthError: '长度不能超过20个字符',
|
||||||
|
cloneName: '分身名称',
|
||||||
|
cloneNamePlaceholder: '例如:自动备份 测试版',
|
||||||
|
cloneNameHint: '分身插件的显示名称(可选)',
|
||||||
|
cloneDefaultName: '{name} 分身',
|
||||||
|
cloneDescriptionLabel: '分身描述',
|
||||||
|
cloneDescriptionPlaceholder: '描述这个分身的用途和特点...',
|
||||||
|
cloneDescriptionHint: '详细描述分身插件的用途(可选)',
|
||||||
|
cloneDefaultDescription: '{description} (分身版本)',
|
||||||
|
cloneVersion: '版本号',
|
||||||
|
cloneVersionPlaceholder: '例如:1.0、2.1.0',
|
||||||
|
cloneVersionHint: '自定义分身插件的版本号(可选)',
|
||||||
|
cloneIcon: '图标URL',
|
||||||
|
cloneIconPlaceholder: 'https://example.com/icon.png',
|
||||||
|
cloneIconHint: '自定义分身插件的图标(可选)',
|
||||||
|
cloneNotice: '分身插件创建后默认为禁用状态,需要手动配置启用。分身后缀一旦确定无法修改。',
|
||||||
|
createClone: '创建分身',
|
||||||
|
cloning: '正在创建 {name} 的分身...',
|
||||||
|
cloneSuccess: '插件分身 {name} 创建成功!',
|
||||||
|
cloneFailed: '插件分身创建失败:{message}',
|
||||||
|
cloneFailedGeneral: '插件分身创建失败',
|
||||||
|
logTitle: '插件日志',
|
||||||
},
|
},
|
||||||
profile: {
|
profile: {
|
||||||
personalInfo: '个人信息',
|
personalInfo: '个人信息',
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ export default {
|
|||||||
unsubscribe: '取消訂閱',
|
unsubscribe: '取消訂閱',
|
||||||
media: '媒體',
|
media: '媒體',
|
||||||
unknown: '未知',
|
unknown: '未知',
|
||||||
|
notice: '注意',
|
||||||
|
itemsPerPage: '每頁條數',
|
||||||
|
pageText: '{0}-{1} 共 {2} 條',
|
||||||
|
noDataText: '沒有數據',
|
||||||
|
loadingText: '加載中...',
|
||||||
},
|
},
|
||||||
mediaType: {
|
mediaType: {
|
||||||
movie: '電影',
|
movie: '電影',
|
||||||
@@ -202,6 +207,10 @@ export default {
|
|||||||
title: '服務',
|
title: '服務',
|
||||||
description: '定時作業',
|
description: '定時作業',
|
||||||
},
|
},
|
||||||
|
cache: {
|
||||||
|
title: '緩存',
|
||||||
|
description: '種子緩存、識別媒體數據緩存、圖片文件緩存管理',
|
||||||
|
},
|
||||||
notification: {
|
notification: {
|
||||||
title: '通知',
|
title: '通知',
|
||||||
description: '通知渠道(微信、Telegram、Slack、SynologyChat、VoceChat、WebPush)、消息發送範圍',
|
description: '通知渠道(微信、Telegram、Slack、SynologyChat、VoceChat、WebPush)、消息發送範圍',
|
||||||
@@ -851,8 +860,8 @@ export default {
|
|||||||
browserSimulation: '使用瀏覽器模擬真實訪問該站點',
|
browserSimulation: '使用瀏覽器模擬真實訪問該站點',
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
add: '新增',
|
add: '新增站點',
|
||||||
edit: '編輯',
|
edit: '編輯站點',
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
addSuccess: '新增站點成功',
|
addSuccess: '新增站點成功',
|
||||||
@@ -938,7 +947,7 @@ export default {
|
|||||||
system: {
|
system: {
|
||||||
custom: '自定義',
|
custom: '自定義',
|
||||||
basicSettings: '基礎設置',
|
basicSettings: '基礎設置',
|
||||||
basicSettingsDesc: '設置服務器的全局功能。',
|
basicSettingsDesc: '設置服務器的全局功能',
|
||||||
appDomain: '訪問域名',
|
appDomain: '訪問域名',
|
||||||
appDomainHint: '用於發送通知時,添加快捷跳轉地址',
|
appDomainHint: '用於發送通知時,添加快捷跳轉地址',
|
||||||
wallpaper: '背景壁紙',
|
wallpaper: '背景壁紙',
|
||||||
@@ -1087,10 +1096,16 @@ export default {
|
|||||||
securityImageDomainsHint: '允許緩存的圖片域名白名單,用於控制可信任的圖片來源',
|
securityImageDomainsHint: '允許緩存的圖片域名白名單,用於控制可信任的圖片來源',
|
||||||
noSecurityImageDomains: '暫無安全域名',
|
noSecurityImageDomains: '暫無安全域名',
|
||||||
securityImageDomainAdd: '添加域名,如:image.tmdb.org',
|
securityImageDomainAdd: '添加域名,如:image.tmdb.org',
|
||||||
|
proxyHost: '代理服務器',
|
||||||
|
proxyHostHint: '設置代理服務器地址,支持:http(s)、socks5、socks5h 等協議',
|
||||||
|
moviePilotAutoUpdate: '自動更新MoviePilot',
|
||||||
|
moviePilotAutoUpdateHint: '重啟時自動更新MoviePilot到最新發行版本',
|
||||||
|
autoUpdateResource: '自動更新站點資源',
|
||||||
|
autoUpdateResourceHint: '重啟時自動檢測和更新站點資源包',
|
||||||
},
|
},
|
||||||
site: {
|
site: {
|
||||||
siteSync: '站點同步',
|
siteSync: '站點同步',
|
||||||
siteSyncDesc: '從CookieCloud快速同步站點數據。',
|
siteSyncDesc: '從CookieCloud快速同步站點數據',
|
||||||
enableLocalCookieCloud: '啟用本地CookieCloud服務器',
|
enableLocalCookieCloud: '啟用本地CookieCloud服務器',
|
||||||
enableLocalCookieCloudHint: '使用內建CookieCloud服務同步站點數據,服務地址為:http://localhost:3000/cookiecloud',
|
enableLocalCookieCloudHint: '使用內建CookieCloud服務同步站點數據,服務地址為:http://localhost:3000/cookiecloud',
|
||||||
serviceAddress: '服務地址',
|
serviceAddress: '服務地址',
|
||||||
@@ -1133,7 +1148,7 @@ export default {
|
|||||||
},
|
},
|
||||||
notification: {
|
notification: {
|
||||||
channels: '通知渠道',
|
channels: '通知渠道',
|
||||||
channelsDesc: '設置消息發送渠道參數。',
|
channelsDesc: '設置消息發送渠道參數',
|
||||||
organizeSuccess: '資源入庫',
|
organizeSuccess: '資源入庫',
|
||||||
downloadAdded: '資源下載',
|
downloadAdded: '資源下載',
|
||||||
subscribeAdded: '添加訂閱',
|
subscribeAdded: '添加訂閱',
|
||||||
@@ -1180,7 +1195,7 @@ export default {
|
|||||||
},
|
},
|
||||||
words: {
|
words: {
|
||||||
customIdentifiers: '自定義識別詞',
|
customIdentifiers: '自定義識別詞',
|
||||||
identifiersDesc: '添加規則對種子名或者文件名進行預處理以校正識別。',
|
identifiersDesc: '添加規則對種子名或者文件名進行預處理以校正識別',
|
||||||
identifiersPlaceholder: '支持正則表達式,特殊字符需要\\轉義,一行為一組',
|
identifiersPlaceholder: '支持正則表達式,特殊字符需要\\轉義,一行為一組',
|
||||||
identifiersHint: '支持正則表達式,特殊字符需要\\轉義,一行為一組',
|
identifiersHint: '支持正則表達式,特殊字符需要\\轉義,一行為一組',
|
||||||
formatTitle: '支持的配置格式(注意空格):',
|
formatTitle: '支持的配置格式(注意空格):',
|
||||||
@@ -1216,7 +1231,7 @@ export default {
|
|||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
basicSettings: '基礎設置',
|
basicSettings: '基礎設置',
|
||||||
basicSettingsDesc: '設定數據源、規則組等基礎信息。',
|
basicSettingsDesc: '設定數據源、規則組等基礎信息',
|
||||||
recognizeSource: '識別數據源',
|
recognizeSource: '識別數據源',
|
||||||
recognizeSourceDesc: '默認使用TMDB。豆瓣識別中文作品通常更友好,但有些國外作品信息不完整。',
|
recognizeSourceDesc: '默認使用TMDB。豆瓣識別中文作品通常更友好,但有些國外作品信息不完整。',
|
||||||
themoviedb: 'TheMovieDb',
|
themoviedb: 'TheMovieDb',
|
||||||
@@ -1252,7 +1267,7 @@ export default {
|
|||||||
},
|
},
|
||||||
directory: {
|
directory: {
|
||||||
storage: '存儲',
|
storage: '存儲',
|
||||||
storageDesc: '設置本地或網盤存儲。',
|
storageDesc: '設置本地或網盤存儲',
|
||||||
directory: '目錄',
|
directory: '目錄',
|
||||||
directoryDesc: '設置媒體文件整理目錄結構,按先後順序依次匹配。',
|
directoryDesc: '設置媒體文件整理目錄結構,按先後順序依次匹配。',
|
||||||
organizeAndScrap: '整理 & 刮削',
|
organizeAndScrap: '整理 & 刮削',
|
||||||
@@ -1313,7 +1328,7 @@ export default {
|
|||||||
},
|
},
|
||||||
scheduler: {
|
scheduler: {
|
||||||
scheduledTasks: '定時作業',
|
scheduledTasks: '定時作業',
|
||||||
scheduledTasksDesc: '包含系統內置服務以及插件提供的服務,手動執行不會影響作業正常的時間表。',
|
scheduledTasksDesc: '包含系統內置服務以及插件提供的服務',
|
||||||
provider: '提供者',
|
provider: '提供者',
|
||||||
taskName: '任務名稱',
|
taskName: '任務名稱',
|
||||||
taskStatus: '任務狀態',
|
taskStatus: '任務狀態',
|
||||||
@@ -1360,6 +1375,56 @@ export default {
|
|||||||
settingsSaveSuccess: '訂閱基礎設置保存成功',
|
settingsSaveSuccess: '訂閱基礎設置保存成功',
|
||||||
settingsSaveFailed: '訂閱基礎設置保存失敗!',
|
settingsSaveFailed: '訂閱基礎設置保存失敗!',
|
||||||
},
|
},
|
||||||
|
cache: {
|
||||||
|
title: '緩存',
|
||||||
|
description: '種子緩存、圖片文件緩存管理',
|
||||||
|
subtitle: '管理緩存的站點資源',
|
||||||
|
filterByTitle: '按標題篩選',
|
||||||
|
filterBySite: '按站點篩選',
|
||||||
|
selectSite: '選擇站點',
|
||||||
|
refresh: '刷新緩存',
|
||||||
|
deleteSelected: '刪除選中',
|
||||||
|
clearAll: '清空緩存',
|
||||||
|
refreshSuccess: '緩存刷新完成',
|
||||||
|
refreshFailed: '刷新緩存失敗',
|
||||||
|
clearSuccess: '緩存清理完成',
|
||||||
|
clearFailed: '清理緩存失敗',
|
||||||
|
deleteSuccess: '緩存項刪除成功',
|
||||||
|
deleteFailed: '刪除緩存項失敗',
|
||||||
|
deleteSelectedSuccess: '成功刪除 {count} 個緩存項',
|
||||||
|
deleteSelectedFailed: '刪除緩存項失敗',
|
||||||
|
loadFailed: '加載緩存數據失敗',
|
||||||
|
selectDeleteWarning: '請選擇要刪除的緩存項',
|
||||||
|
reidentify: '重新識別',
|
||||||
|
reidentifySuccess: '重新識別完成',
|
||||||
|
reidentifyFailed: '重新識別失敗',
|
||||||
|
poster: '海報',
|
||||||
|
torrentTitle: '標題',
|
||||||
|
site: '站點',
|
||||||
|
size: '大小',
|
||||||
|
publishTime: '發布時間',
|
||||||
|
recognitionResult: '識別結果',
|
||||||
|
actions: '操作',
|
||||||
|
unrecognized: '未識別',
|
||||||
|
noData: '暫無緩存數據',
|
||||||
|
noDataHint: '點擊"刷新緩存"按鈕獲取最新的種子緩存',
|
||||||
|
reidentifyDialog: {
|
||||||
|
title: '重新識別',
|
||||||
|
torrentInfo: '種子信息',
|
||||||
|
tmdbId: 'TMDB ID',
|
||||||
|
tmdbIdHint: '可選,手動指定TMDB ID進行識別',
|
||||||
|
doubanId: '豆瓣 ID',
|
||||||
|
doubanIdHint: '可選,手動指定豆瓣ID進行識別',
|
||||||
|
autoHint: '如果不指定ID,將自動重新識別該種子',
|
||||||
|
cancel: '取消',
|
||||||
|
confirm: '重新識別',
|
||||||
|
},
|
||||||
|
mediaType: {
|
||||||
|
movie: '電影',
|
||||||
|
tv: '電視劇',
|
||||||
|
},
|
||||||
|
clearConfirm: '確認清空所有緩存嗎?',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
progress: {
|
progress: {
|
||||||
@@ -1681,8 +1746,8 @@ export default {
|
|||||||
previous: '上一步',
|
previous: '上一步',
|
||||||
confirm: '確認',
|
confirm: '確認',
|
||||||
manualTitle: '手動整理',
|
manualTitle: '手動整理',
|
||||||
multipleItemsTitle: '整理 - 共 {count} 項',
|
multipleItemsTitle: '共 {count} 項',
|
||||||
singleItemTitle: '整理 - {path}',
|
singleItemTitle: '{path}',
|
||||||
targetStorage: '目的存儲',
|
targetStorage: '目的存儲',
|
||||||
targetStorageHint: '整理目的存儲',
|
targetStorageHint: '整理目的存儲',
|
||||||
transferType: '整理方式',
|
transferType: '整理方式',
|
||||||
@@ -1731,7 +1796,7 @@ export default {
|
|||||||
},
|
},
|
||||||
subscribeEdit: {
|
subscribeEdit: {
|
||||||
titleDefault: '默認訂閱規則',
|
titleDefault: '默認訂閱規則',
|
||||||
titleEditFormat: '編輯訂閱 - {name} {season}',
|
titleEdit: '編輯訂閱',
|
||||||
seasonFormat: '第 {number} 季',
|
seasonFormat: '第 {number} 季',
|
||||||
tabs: {
|
tabs: {
|
||||||
basic: '基礎',
|
basic: '基礎',
|
||||||
@@ -1990,6 +2055,38 @@ export default {
|
|||||||
folderName: '文件夾名稱',
|
folderName: '文件夾名稱',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
create: '創建',
|
create: '創建',
|
||||||
|
clone: '分身',
|
||||||
|
cloneTitle: '創建插件分身',
|
||||||
|
cloneSubtitle: '為 {name} 創建獨立的分身實例',
|
||||||
|
cloneFeature: '插件分身功能',
|
||||||
|
cloneDescription: '創建插件的獨立副本,擁有獨立的配置和數據,適用於多賬號、測試環境等場景',
|
||||||
|
suffix: '分身後綴',
|
||||||
|
suffixPlaceholder: '例如:Test、Backup、Site1',
|
||||||
|
suffixHint: '用於區分分身的唯一標識,只能包含英文字母和數字',
|
||||||
|
suffixRequired: '分身後綴不能為空',
|
||||||
|
suffixFormatError: '只能包含英文字母和數字',
|
||||||
|
suffixLengthError: '長度不能超過20個字符',
|
||||||
|
cloneName: '分身名稱',
|
||||||
|
cloneNamePlaceholder: '例如:自動備份 測試版',
|
||||||
|
cloneNameHint: '分身插件的顯示名稱(可選)',
|
||||||
|
cloneDefaultName: '{name} 分身',
|
||||||
|
cloneDescriptionLabel: '分身描述',
|
||||||
|
cloneDescriptionPlaceholder: '描述這個分身的用途和特點...',
|
||||||
|
cloneDescriptionHint: '詳細描述分身插件的用途(可選)',
|
||||||
|
cloneDefaultDescription: '{description} (分身版本)',
|
||||||
|
cloneVersion: '版本號',
|
||||||
|
cloneVersionPlaceholder: '例如:1.0、2.1.0',
|
||||||
|
cloneVersionHint: '自定義分身插件的版本號(可選)',
|
||||||
|
cloneIcon: '圖標URL',
|
||||||
|
cloneIconPlaceholder: 'https://example.com/icon.png',
|
||||||
|
cloneIconHint: '自定義分身插件的圖標(可選)',
|
||||||
|
cloneNotice: '分身插件創建後默認為禁用狀態,需要手動配置啟用。分身後綴一旦確定無法修改。',
|
||||||
|
createClone: '創建分身',
|
||||||
|
cloning: '正在創建 {name} 的分身...',
|
||||||
|
cloneSuccess: '插件分身 {name} 創建成功!',
|
||||||
|
cloneFailed: '插件分身創建失敗:{message}',
|
||||||
|
cloneFailedGeneral: '插件分身創建失敗',
|
||||||
|
logTitle: '插件日誌',
|
||||||
},
|
},
|
||||||
profile: {
|
profile: {
|
||||||
personalInfo: '個人信息',
|
personalInfo: '個人信息',
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PersonCardListView from '@/views/discover/PersonCardListView.vue'
|
import PersonCardListView from '@/views/discover/PersonCardListView.vue'
|
||||||
|
|
||||||
|
// 输入参数
|
||||||
|
const props = defineProps({
|
||||||
|
// API路径
|
||||||
|
paths: Array as PropType<string[]> | PropType<string>,
|
||||||
|
})
|
||||||
|
|
||||||
// 路由参数
|
// 路由参数
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const id = route.query?.id?.toString()
|
|
||||||
const title = route.query?.title?.toString()
|
// 标题
|
||||||
const source = route.query?.source?.toString()
|
let title = route.query?.title?.toString()
|
||||||
const type = route.query?.type?.toString()
|
|
||||||
const apipath = route.query?.apipath?.toString()
|
// 计算API路径
|
||||||
|
function getApiPath(paths: string[] | string) {
|
||||||
|
if (Array.isArray(paths)) return paths.join('/')
|
||||||
|
else return paths
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VPageContentTitle :title="title" />
|
<VPageContentTitle :title="title" />
|
||||||
<PersonCardListView
|
<PersonCardListView :apipath="getApiPath(props.paths || '')" />
|
||||||
:credits-id="id"
|
|
||||||
:credits-name="title"
|
|
||||||
:credits-source="source"
|
|
||||||
:credits-type="type"
|
|
||||||
:credits-apipath="apipath"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -14,12 +14,6 @@ const mediaid = route.query?.mediaid?.toString()
|
|||||||
// 类型:电影、电视剧
|
// 类型:电影、电视剧
|
||||||
const type = route.query?.type?.toString()
|
const type = route.query?.type?.toString()
|
||||||
|
|
||||||
// 媒体信息来源:TMDB、豆瓣
|
|
||||||
const source = route.query?.source?.toString() || 'themoviedb'
|
|
||||||
|
|
||||||
// TMDB ID
|
|
||||||
const page = route.query?.page?.toString() || '1'
|
|
||||||
|
|
||||||
// 标题
|
// 标题
|
||||||
const title = route.query?.title?.toString()
|
const title = route.query?.title?.toString()
|
||||||
|
|
||||||
@@ -29,6 +23,6 @@ const year = route.query?.year?.toString()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<MediaDetailView :mediaid="mediaid" :type="type" :source="source" :page="page" :title="title" :year="year" />
|
<MediaDetailView :mediaid="mediaid" :type="type" :title="title" :year="year" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import AccountSettingAbout from '@/views/setting/AccountSettingAbout.vue'
|
|||||||
import AccountSettingSearch from '@/views/setting/AccountSettingSearch.vue'
|
import AccountSettingSearch from '@/views/setting/AccountSettingSearch.vue'
|
||||||
import AccountSettingSubscribe from '@/views/setting/AccountSettingSubscribe.vue'
|
import AccountSettingSubscribe from '@/views/setting/AccountSettingSubscribe.vue'
|
||||||
import AccountSettingSystem from '@/views/setting/AccountSettingSystem.vue'
|
import AccountSettingSystem from '@/views/setting/AccountSettingSystem.vue'
|
||||||
import AccountSettingScheduler from '@/views/setting/AccountSettingScheduler.vue'
|
import AccountSettingService from '@/views/setting/AccountSettingService.vue'
|
||||||
import AccountSettingDirectory from '@/views/setting/AccountSettingDirectory.vue'
|
import AccountSettingDirectory from '@/views/setting/AccountSettingDirectory.vue'
|
||||||
import AccountSettingRule from '@/views/setting/AccountSettingRule.vue'
|
import AccountSettingRule from '@/views/setting/AccountSettingRule.vue'
|
||||||
|
import AccountSettingCache from '@/views/setting/AccountSettingCache.vue'
|
||||||
import { getSettingTabs } from '@/router/i18n-menu'
|
import { getSettingTabs } from '@/router/i18n-menu'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -81,7 +82,16 @@ const settingTabs = computed(() => getSettingTabs())
|
|||||||
<VWindowItem value="scheduler">
|
<VWindowItem value="scheduler">
|
||||||
<transition name="fade-slide" appear>
|
<transition name="fade-slide" appear>
|
||||||
<div>
|
<div>
|
||||||
<AccountSettingScheduler />
|
<AccountSettingService />
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</VWindowItem>
|
||||||
|
|
||||||
|
<!-- 缓存 -->
|
||||||
|
<VWindowItem value="cache">
|
||||||
|
<transition name="fade-slide" appear>
|
||||||
|
<div>
|
||||||
|
<AccountSettingCache />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</VWindowItem>
|
</VWindowItem>
|
||||||
|
|||||||
@@ -170,6 +170,12 @@ export function getSettingTabs() {
|
|||||||
tab: 'scheduler',
|
tab: 'scheduler',
|
||||||
description: t('settingTabs.scheduler.description'),
|
description: t('settingTabs.scheduler.description'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('settingTabs.cache.title'),
|
||||||
|
icon: 'mdi-database',
|
||||||
|
tab: 'cache',
|
||||||
|
description: t('settingTabs.cache.description'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('settingTabs.notification.title'),
|
title: t('settingTabs.notification.title'),
|
||||||
icon: 'mdi-bell',
|
icon: 'mdi-bell',
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ async function fetchData({ done }: { done: any }) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
||||||
<VInfiniteScroll mode="intersect" side="end" :items="dataList" class="overflow-visible" @load="fetchData">
|
<VInfiniteScroll mode="intersect" side="end" :items="dataList" class="overflow-visible px-3" @load="fetchData">
|
||||||
<template #loading />
|
<template #loading />
|
||||||
<template #empty />
|
<template #empty />
|
||||||
<div v-if="dataList.length > 0" class="grid gap-4 grid-media-card" tabindex="0">
|
<div v-if="dataList.length > 0" class="grid gap-4 grid-media-card" tabindex="0">
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ onBeforeMount(() => {
|
|||||||
'ring-1 ring-gray-700': isImageLoaded,
|
'ring-1 ring-gray-700': isImageLoaded,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<VImg v-img :src="getPersonImage()" cover @load="isImageLoaded = true" />
|
<VImg :src="getPersonImage()" cover @load="isImageLoaded = true" />
|
||||||
</VAvatar>
|
</VAvatar>
|
||||||
<div class="ms-3">
|
<div class="ms-3">
|
||||||
<h1 class="text-3xl lg:text-4xl text-center text-lg-left">
|
<h1 class="text-3xl lg:text-4xl text-center text-lg-left">
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import api from '@/api'
|
|||||||
import type { Plugin } from '@/api/types'
|
import type { Plugin } from '@/api/types'
|
||||||
import NoDataFound from '@/components/NoDataFound.vue'
|
import NoDataFound from '@/components/NoDataFound.vue'
|
||||||
import PluginAppCard from '@/components/cards/PluginAppCard.vue'
|
import PluginAppCard from '@/components/cards/PluginAppCard.vue'
|
||||||
import PluginCard from '@/components/cards/PluginCard.vue'
|
|
||||||
import noImage from '@images/logos/plugin.png'
|
import noImage from '@images/logos/plugin.png'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import { isNullOrEmptyObject } from '@/@core/utils'
|
import { isNullOrEmptyObject } from '@/@core/utils'
|
||||||
@@ -13,7 +12,7 @@ import { getPluginTabs } from '@/router/i18n-menu'
|
|||||||
import PluginMarketSettingDialog from '@/components/dialog/PluginMarketSettingDialog.vue'
|
import PluginMarketSettingDialog from '@/components/dialog/PluginMarketSettingDialog.vue'
|
||||||
import { useDynamicButton } from '@/composables/useDynamicButton'
|
import { useDynamicButton } from '@/composables/useDynamicButton'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import PluginFolderCard from '@/components/cards/PluginFolderCard.vue'
|
import PluginMixedSortCard from '@/components/cards/PluginMixedSortCard.vue'
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -39,7 +38,7 @@ const pluginId = ref(route.query.id)
|
|||||||
const activeSort = ref(null)
|
const activeSort = ref(null)
|
||||||
|
|
||||||
// 插件顺序配置
|
// 插件顺序配置
|
||||||
const orderConfig = ref<{ id: string }[]>([])
|
const orderConfig = ref<{ id: string; type?: string; order?: number }[]>([])
|
||||||
|
|
||||||
// 排序选项
|
// 排序选项
|
||||||
const sortOptions = computed(() => [
|
const sortOptions = computed(() => [
|
||||||
@@ -176,6 +175,33 @@ const newFolderDialog = ref(false)
|
|||||||
// 新文件夹名称
|
// 新文件夹名称
|
||||||
const newFolderName = ref('')
|
const newFolderName = ref('')
|
||||||
|
|
||||||
|
// 获取文件夹内筛选后的插件
|
||||||
|
const getFilteredFolderPlugins = (folderName: string) => {
|
||||||
|
const folderData = pluginFolders.value[folderName]
|
||||||
|
const folderPluginIds = Array.isArray(folderData) ? folderData : folderData?.plugins || []
|
||||||
|
|
||||||
|
// 获取文件夹内的插件并应用筛选条件
|
||||||
|
const folderPlugins: Plugin[] = []
|
||||||
|
folderPluginIds.forEach((pluginId: string) => {
|
||||||
|
const plugin = dataList.value.find(p => p.id === pluginId)
|
||||||
|
if (plugin) {
|
||||||
|
folderPlugins.push(plugin)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 应用筛选条件
|
||||||
|
return folderPlugins.filter(plugin => {
|
||||||
|
if (!installedFilter.value && !hasUpdateFilter.value) return true
|
||||||
|
if (hasUpdateFilter.value) {
|
||||||
|
return plugin.has_update
|
||||||
|
}
|
||||||
|
if (installedFilter.value) {
|
||||||
|
return plugin.plugin_name?.toLowerCase().includes((installedFilter.value as string).toLowerCase())
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 显示的插件列表(考虑文件夹筛选)
|
// 显示的插件列表(考虑文件夹筛选)
|
||||||
const displayedPlugins = computed(() => {
|
const displayedPlugins = computed(() => {
|
||||||
if (!currentFolder.value) {
|
if (!currentFolder.value) {
|
||||||
@@ -187,15 +213,21 @@ const displayedPlugins = computed(() => {
|
|||||||
})
|
})
|
||||||
return filteredDataList.value.filter(plugin => !folderedPluginIds.has(plugin.id))
|
return filteredDataList.value.filter(plugin => !folderedPluginIds.has(plugin.id))
|
||||||
} else {
|
} else {
|
||||||
// 文件夹内:只显示文件夹中的插件
|
// 文件夹内:返回筛选后的插件
|
||||||
const folderData = pluginFolders.value[currentFolder.value]
|
return getFilteredFolderPlugins(currentFolder.value)
|
||||||
const folderPluginIds = Array.isArray(folderData) ? folderData : folderData?.plugins || []
|
|
||||||
return filteredDataList.value.filter(plugin => folderPluginIds.includes(plugin.id))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 可拖拽的插件列表(主列表用)
|
// 混合排序项目类型
|
||||||
const draggableMainPlugins = ref<Plugin[]>([])
|
interface MixedSortItem {
|
||||||
|
type: 'folder' | 'plugin'
|
||||||
|
id: string
|
||||||
|
data: any
|
||||||
|
order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 混合排序列表(包含文件夹和插件)
|
||||||
|
const mixedSortList = ref<MixedSortItem[]>([])
|
||||||
|
|
||||||
// 可拖拽的插件列表(文件夹内用)
|
// 可拖拽的插件列表(文件夹内用)
|
||||||
const draggableFolderPlugins = ref<Plugin[]>([])
|
const draggableFolderPlugins = ref<Plugin[]>([])
|
||||||
@@ -203,30 +235,6 @@ const draggableFolderPlugins = ref<Plugin[]>([])
|
|||||||
// 是否正在拖拽排序中
|
// 是否正在拖拽排序中
|
||||||
const isDraggingSortMode = ref(false)
|
const isDraggingSortMode = ref(false)
|
||||||
|
|
||||||
// 监听displayedPlugins变化,更新可拖拽列表(避免拖拽时的循环更新)
|
|
||||||
watch(
|
|
||||||
displayedPlugins,
|
|
||||||
newPlugins => {
|
|
||||||
if (isDraggingSortMode.value) return // 拖拽排序时跳过更新
|
|
||||||
|
|
||||||
if (!currentFolder.value) {
|
|
||||||
draggableMainPlugins.value = [...newPlugins]
|
|
||||||
} else {
|
|
||||||
draggableFolderPlugins.value = [...newPlugins]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
// 监听文件夹切换,更新可拖拽列表
|
|
||||||
watch(currentFolder, () => {
|
|
||||||
if (!currentFolder.value) {
|
|
||||||
draggableMainPlugins.value = [...displayedPlugins.value]
|
|
||||||
} else {
|
|
||||||
draggableFolderPlugins.value = [...displayedPlugins.value]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 显示的文件夹列表(按排序显示)
|
// 显示的文件夹列表(按排序显示)
|
||||||
const displayedFolders = computed(() => {
|
const displayedFolders = computed(() => {
|
||||||
if (currentFolder.value) return [] // 在文件夹内不显示其他文件夹
|
if (currentFolder.value) return [] // 在文件夹内不显示其他文件夹
|
||||||
@@ -239,17 +247,108 @@ const displayedFolders = computed(() => {
|
|||||||
const unsortedFolders = folderNames.filter(name => !folderOrder.value.includes(name))
|
const unsortedFolders = folderNames.filter(name => !folderOrder.value.includes(name))
|
||||||
sortedFolderNames.push(...unsortedFolders)
|
sortedFolderNames.push(...unsortedFolders)
|
||||||
|
|
||||||
return sortedFolderNames.map(folderName => {
|
return sortedFolderNames
|
||||||
const folderData = pluginFolders.value[folderName]
|
.map(folderName => {
|
||||||
const plugins = Array.isArray(folderData) ? folderData : folderData?.plugins || []
|
const folderData = pluginFolders.value[folderName]
|
||||||
const config = Array.isArray(folderData) ? {} : folderData
|
const config = Array.isArray(folderData) ? {} : folderData
|
||||||
|
|
||||||
return {
|
// 获取筛选后的插件数量
|
||||||
name: folderName,
|
const filteredPlugins = getFilteredFolderPlugins(folderName)
|
||||||
pluginCount: plugins.length,
|
|
||||||
config: config,
|
return {
|
||||||
|
name: folderName,
|
||||||
|
pluginCount: filteredPlugins.length,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(folder => {
|
||||||
|
// 当有筛选条件时,只显示包含筛选后插件的文件夹
|
||||||
|
if (installedFilter.value || hasUpdateFilter.value) {
|
||||||
|
return folder.pluginCount > 0
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新混合排序列表
|
||||||
|
function updateMixedSortList() {
|
||||||
|
if (isDraggingSortMode.value) return // 拖拽排序时跳过更新
|
||||||
|
|
||||||
|
if (!currentFolder.value) {
|
||||||
|
// 主列表:创建混合列表
|
||||||
|
const items: MixedSortItem[] = []
|
||||||
|
|
||||||
|
// 创建统一的排序索引
|
||||||
|
let globalOrder = 0
|
||||||
|
|
||||||
|
// 始终使用全局排序配置来创建混合列表
|
||||||
|
const allItems: { type: 'folder' | 'plugin'; id: string; data: any; order: number }[] = []
|
||||||
|
|
||||||
|
// 添加文件夹项目
|
||||||
|
displayedFolders.value.forEach(folder => {
|
||||||
|
const orderItem = orderConfig.value.find((item: any) => item.type === 'folder' && item.id === folder.name)
|
||||||
|
allItems.push({
|
||||||
|
type: 'folder',
|
||||||
|
id: folder.name,
|
||||||
|
data: folder,
|
||||||
|
order: orderItem?.order ?? 999,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加插件项目
|
||||||
|
displayedPlugins.value.forEach(plugin => {
|
||||||
|
const orderItem = orderConfig.value.find((item: any) => item.type === 'plugin' && item.id === plugin.id)
|
||||||
|
allItems.push({
|
||||||
|
type: 'plugin',
|
||||||
|
id: plugin.id || '',
|
||||||
|
data: plugin,
|
||||||
|
order: orderItem?.order ?? 999,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 按order排序
|
||||||
|
allItems.sort((a, b) => a.order - b.order)
|
||||||
|
|
||||||
|
// 转换为MixedSortItem格式
|
||||||
|
allItems.forEach((item, index) => {
|
||||||
|
items.push({
|
||||||
|
type: item.type,
|
||||||
|
id: item.id,
|
||||||
|
data: item.data,
|
||||||
|
order: index,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 按order排序
|
||||||
|
items.sort((a, b) => a.order - b.order)
|
||||||
|
mixedSortList.value = items
|
||||||
|
} else {
|
||||||
|
// 文件夹内:只更新插件列表
|
||||||
|
draggableFolderPlugins.value = [...displayedPlugins.value]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听相关数据变化,更新混合排序列表
|
||||||
|
watch(
|
||||||
|
[displayedPlugins, displayedFolders, orderConfig, folderOrder, installedFilter, hasUpdateFilter],
|
||||||
|
() => {
|
||||||
|
// 只有在非拖拽状态下才更新
|
||||||
|
if (!isDraggingSortMode.value) {
|
||||||
|
updateMixedSortList()
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听文件夹切换,更新列表
|
||||||
|
watch(currentFolder, () => {
|
||||||
|
// 只有在非拖拽状态下才更新
|
||||||
|
if (!isDraggingSortMode.value) {
|
||||||
|
updateMixedSortList()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 加载插件顺序
|
// 加载插件顺序
|
||||||
@@ -257,11 +356,33 @@ async function loadPluginOrderConfig() {
|
|||||||
// 顺序配置
|
// 顺序配置
|
||||||
const local_order = localStorage.getItem('MP_PLUGIN_ORDER')
|
const local_order = localStorage.getItem('MP_PLUGIN_ORDER')
|
||||||
if (local_order) {
|
if (local_order) {
|
||||||
orderConfig.value = JSON.parse(local_order)
|
const parsed = JSON.parse(local_order)
|
||||||
|
// 兼容旧格式(只有id)和新格式(包含type和order)
|
||||||
|
if (parsed.length > 0 && typeof parsed[0] === 'object' && 'type' in parsed[0]) {
|
||||||
|
orderConfig.value = parsed
|
||||||
|
} else {
|
||||||
|
// 旧格式,转换为新格式
|
||||||
|
orderConfig.value = parsed.map((item: any, index: number) => ({
|
||||||
|
id: typeof item === 'string' ? item : item.id,
|
||||||
|
type: 'plugin',
|
||||||
|
order: index,
|
||||||
|
}))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const response2 = await api.get('/user/config/PluginOrder')
|
const response2 = await api.get('/user/config/PluginOrder')
|
||||||
if (response2 && response2.data && response2.data.value) {
|
if (response2 && response2.data && response2.data.value) {
|
||||||
orderConfig.value = response2.data.value
|
const serverData = response2.data.value
|
||||||
|
// 兼容服务端的旧格式和新格式
|
||||||
|
if (serverData.length > 0 && typeof serverData[0] === 'object' && 'type' in serverData[0]) {
|
||||||
|
orderConfig.value = serverData
|
||||||
|
} else {
|
||||||
|
// 旧格式,转换为新格式
|
||||||
|
orderConfig.value = serverData.map((item: any, index: number) => ({
|
||||||
|
id: typeof item === 'string' ? item : item.id,
|
||||||
|
type: 'plugin',
|
||||||
|
order: index,
|
||||||
|
}))
|
||||||
|
}
|
||||||
localStorage.setItem('MP_PLUGIN_ORDER', JSON.stringify(orderConfig.value))
|
localStorage.setItem('MP_PLUGIN_ORDER', JSON.stringify(orderConfig.value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,56 +403,75 @@ function sortPluginOrder() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存顺序设置
|
// 保存混合排序
|
||||||
async function savePluginOrder() {
|
async function saveMixedSortOrder() {
|
||||||
// 只在主列表中保存顺序,文件夹内不保存全局顺序
|
|
||||||
if (currentFolder.value) return
|
|
||||||
|
|
||||||
// 顺序配置
|
|
||||||
const orderObj = filteredDataList.value.map(item => ({ id: item.id || '' }))
|
|
||||||
orderConfig.value = orderObj
|
|
||||||
const orderString = JSON.stringify(orderObj)
|
|
||||||
localStorage.setItem('MP_PLUGIN_ORDER', orderString)
|
|
||||||
|
|
||||||
// 保存到服务端
|
|
||||||
try {
|
try {
|
||||||
await api.post('/user/config/PluginOrder', orderObj)
|
// 分离文件夹和插件,并记录它们的全局排序位置
|
||||||
} catch (error) {
|
const newFolderOrder: string[] = []
|
||||||
console.error(error)
|
const newPluginOrder: Plugin[] = []
|
||||||
}
|
const globalOrder: { type: 'folder' | 'plugin'; id: string; order: number }[] = []
|
||||||
}
|
|
||||||
|
|
||||||
// 保存主列表插件顺序
|
mixedSortList.value.forEach((item, index) => {
|
||||||
async function saveMainPluginOrder() {
|
globalOrder.push({
|
||||||
try {
|
type: item.type,
|
||||||
// 更新主列表数据
|
id: item.id,
|
||||||
const newOrderedList = [...draggableMainPlugins.value]
|
order: index,
|
||||||
|
})
|
||||||
|
|
||||||
// 添加文件夹中的插件到末尾
|
if (item.type === 'folder') {
|
||||||
|
newFolderOrder.push(item.id)
|
||||||
|
} else if (item.type === 'plugin') {
|
||||||
|
newPluginOrder.push(item.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新文件夹排序并设置order属性
|
||||||
|
folderOrder.value = newFolderOrder
|
||||||
|
newFolderOrder.forEach((folderName, index) => {
|
||||||
|
if (pluginFolders.value[folderName]) {
|
||||||
|
// 找到该文件夹在全局排序中的位置
|
||||||
|
const globalOrderItem = globalOrder.find(item => item.type === 'folder' && item.id === folderName)
|
||||||
|
pluginFolders.value[folderName].order = globalOrderItem ? globalOrderItem.order : index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加文件夹中的插件到插件列表末尾
|
||||||
Object.values(pluginFolders.value).forEach(folderData => {
|
Object.values(pluginFolders.value).forEach(folderData => {
|
||||||
const plugins = Array.isArray(folderData) ? folderData : folderData.plugins || []
|
const plugins = Array.isArray(folderData) ? folderData : folderData.plugins || []
|
||||||
plugins.forEach((id: string) => {
|
plugins.forEach((id: string) => {
|
||||||
const folderPlugin = dataList.value.find(p => p.id === id)
|
const folderPlugin = dataList.value.find(p => p.id === id)
|
||||||
if (folderPlugin && !newOrderedList.find(p => p.id === id)) {
|
if (folderPlugin && !newPluginOrder.find(p => p.id === id)) {
|
||||||
newOrderedList.push(folderPlugin)
|
newPluginOrder.push(folderPlugin)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
filteredDataList.value = newOrderedList
|
// 更新插件列表
|
||||||
|
filteredDataList.value = newPluginOrder
|
||||||
|
|
||||||
// 保存排序配置
|
// 保存插件排序配置(包含全局排序信息)
|
||||||
const orderObj = newOrderedList.map(item => ({ id: item.id || '' }))
|
const orderObj = globalOrder.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
type: item.type,
|
||||||
|
order: item.order,
|
||||||
|
}))
|
||||||
orderConfig.value = orderObj
|
orderConfig.value = orderObj
|
||||||
const orderString = JSON.stringify(orderObj)
|
const orderString = JSON.stringify(orderObj)
|
||||||
localStorage.setItem('MP_PLUGIN_ORDER', orderString)
|
localStorage.setItem('MP_PLUGIN_ORDER', orderString)
|
||||||
|
|
||||||
// 保存到服务端
|
// 保存到服务端
|
||||||
await api.post('/user/config/PluginOrder', orderObj)
|
await api.post('/user/config/PluginOrder', orderObj)
|
||||||
|
|
||||||
|
// 保存文件夹排序
|
||||||
|
await savePluginFolders()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
// 清除拖拽标志
|
// 清除拖拽标志
|
||||||
isDraggingSortMode.value = false
|
isDraggingSortMode.value = false
|
||||||
|
|
||||||
|
// 在清除拖拽标志后更新混合排序列表显示
|
||||||
|
updateMixedSortList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,11 +493,36 @@ async function saveFolderPluginOrder() {
|
|||||||
folderData.plugins = newPluginIds
|
folderData.plugins = newPluginIds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新全局排序配置中文件夹内插件的顺序
|
||||||
|
const folderOrderItem = orderConfig.value.find(
|
||||||
|
(item: any) => item.type === 'folder' && item.id === currentFolder.value,
|
||||||
|
)
|
||||||
|
const folderGlobalOrder = folderOrderItem?.order ?? 999
|
||||||
|
|
||||||
|
// 为文件夹内的插件分配连续的order值
|
||||||
|
newPluginIds.forEach((pluginId, index) => {
|
||||||
|
const existingItem = orderConfig.value.find((item: any) => item.type === 'plugin' && item.id === pluginId)
|
||||||
|
if (existingItem) {
|
||||||
|
existingItem.order = folderGlobalOrder + 0.1 + index * 0.01 // 使用小数确保在文件夹后面
|
||||||
|
} else {
|
||||||
|
orderConfig.value.push({
|
||||||
|
id: pluginId,
|
||||||
|
type: 'plugin',
|
||||||
|
order: folderGlobalOrder + 0.1 + index * 0.01,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 保存全局排序配置
|
||||||
|
const orderString = JSON.stringify(orderConfig.value)
|
||||||
|
localStorage.setItem('MP_PLUGIN_ORDER', orderString)
|
||||||
|
await api.post('/user/config/PluginOrder', orderConfig.value)
|
||||||
|
|
||||||
// 保存到后端
|
// 保存到后端
|
||||||
await savePluginFolders()
|
await savePluginFolders()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存文件夹内排序失败:', error)
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
// 清除拖拽标志
|
// 清除拖拽标志
|
||||||
isDraggingSortMode.value = false
|
isDraggingSortMode.value = false
|
||||||
@@ -523,6 +688,8 @@ async function getPluginStatistics() {
|
|||||||
async function refreshData() {
|
async function refreshData() {
|
||||||
await fetchInstalledPlugins()
|
await fetchInstalledPlugins()
|
||||||
fetchUninstalledPlugins()
|
fetchUninstalledPlugins()
|
||||||
|
// 重新加载文件夹配置,确保分身插件能正确显示在文件夹中
|
||||||
|
await loadPluginFolders()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对uninstalledList进行排序到sortedUninstalledList
|
// 对uninstalledList进行排序到sortedUninstalledList
|
||||||
@@ -680,10 +847,18 @@ async function loadPluginFolders() {
|
|||||||
|
|
||||||
pluginFolders.value = processedFolders
|
pluginFolders.value = processedFolders
|
||||||
|
|
||||||
// 设置文件夹排序
|
// 设置文件夹排序 - 使用全局排序配置
|
||||||
folderOrder.value = Object.keys(processedFolders).sort(
|
const folderNames = Object.keys(processedFolders)
|
||||||
(a, b) => (processedFolders[a].order || 0) - (processedFolders[b].order || 0),
|
folderOrder.value = folderNames.sort((a, b) => {
|
||||||
)
|
// 从全局排序配置中查找文件夹的order
|
||||||
|
const aOrderItem = orderConfig.value.find((item: any) => item.type === 'folder' && item.id === a)
|
||||||
|
const bOrderItem = orderConfig.value.find((item: any) => item.type === 'folder' && item.id === b)
|
||||||
|
|
||||||
|
const aOrder = aOrderItem?.order ?? processedFolders[a].order ?? 999
|
||||||
|
const bOrder = bOrderItem?.order ?? processedFolders[b].order ?? 999
|
||||||
|
|
||||||
|
return aOrder - bOrder
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
pluginFolders.value = {}
|
pluginFolders.value = {}
|
||||||
folderOrder.value = []
|
folderOrder.value = []
|
||||||
@@ -791,7 +966,7 @@ async function renameFolder(oldName: string, newName: string) {
|
|||||||
|
|
||||||
$toast.success(t('plugin.folderRenameSuccess'))
|
$toast.success(t('plugin.folderRenameSuccess'))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('重命名文件夹失败:', error)
|
console.error(error)
|
||||||
// 回滚本地更改
|
// 回滚本地更改
|
||||||
pluginFolders.value[oldName] = pluginFolders.value[newName] || { plugins: [] }
|
pluginFolders.value[oldName] = pluginFolders.value[newName] || { plugins: [] }
|
||||||
delete pluginFolders.value[newName]
|
delete pluginFolders.value[newName]
|
||||||
@@ -885,33 +1060,10 @@ async function updateFolderConfig(folderName: string, config: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文件夹拖拽排序结束事件
|
|
||||||
function onFolderSortEnd() {
|
|
||||||
// 保存新的文件夹顺序
|
|
||||||
savePluginFolders()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前拖拽的插件ID
|
// 当前拖拽的插件ID
|
||||||
const currentDraggedPluginId = ref('')
|
const currentDraggedPluginId = ref('')
|
||||||
|
|
||||||
// 处理拖拽到文件夹的事件
|
// 处理拖拽到文件夹的事件
|
||||||
function handleDragOver(event: DragEvent) {
|
|
||||||
event.preventDefault()
|
|
||||||
event.dataTransfer!.dropEffect = 'move'
|
|
||||||
const target = event.currentTarget as HTMLElement
|
|
||||||
target.classList.add('drag-over')
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragEnter(event: DragEvent) {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragLeave(event: DragEvent) {
|
|
||||||
event.preventDefault()
|
|
||||||
const target = event.currentTarget as HTMLElement
|
|
||||||
target.classList.remove('drag-over')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDropToFolder(event: DragEvent, folderName: string) {
|
async function handleDropToFolder(event: DragEvent, folderName: string) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
@@ -964,9 +1116,9 @@ async function handleDropToFolder(event: DragEvent, folderName: string) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 从主列表中移除(如果存在)
|
// 从主列表中移除(如果存在)
|
||||||
const mainIndex = draggableMainPlugins.value.findIndex(p => p.id === pluginId)
|
const mainIndex = mixedSortList.value.findIndex(item => item.type === 'plugin' && item.id === pluginId)
|
||||||
if (mainIndex > -1) {
|
if (mainIndex > -1) {
|
||||||
draggableMainPlugins.value.splice(mainIndex, 1)
|
mixedSortList.value.splice(mainIndex, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加到目标文件夹
|
// 添加到目标文件夹
|
||||||
@@ -993,9 +1145,11 @@ async function handleDropToFolder(event: DragEvent, folderName: string) {
|
|||||||
// 保存配置
|
// 保存配置
|
||||||
await savePluginFolders()
|
await savePluginFolders()
|
||||||
|
|
||||||
|
// 更新混合排序列表
|
||||||
|
updateMixedSortList()
|
||||||
|
|
||||||
$toast.success(`插件已移动到文件夹 "${folderName}"`)
|
$toast.success(`插件已移动到文件夹 "${folderName}"`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('拖拽到文件夹失败:', error)
|
|
||||||
$toast.error('操作失败')
|
$toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1008,10 +1162,18 @@ function onDragStartPlugin(evt: any) {
|
|||||||
// 从oldIndex获取插件ID
|
// 从oldIndex获取插件ID
|
||||||
const oldIndex = evt.oldIndex
|
const oldIndex = evt.oldIndex
|
||||||
if (oldIndex !== undefined) {
|
if (oldIndex !== undefined) {
|
||||||
const plugin = currentFolder.value ? draggableFolderPlugins.value[oldIndex] : draggableMainPlugins.value[oldIndex]
|
if (currentFolder.value) {
|
||||||
if (plugin && plugin.id) {
|
const plugin = draggableFolderPlugins.value[oldIndex]
|
||||||
currentDraggedPluginId.value = plugin.id
|
if (plugin && plugin.id) {
|
||||||
return
|
currentDraggedPluginId.value = plugin.id
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const item = mixedSortList.value[oldIndex]
|
||||||
|
if (item && item.id) {
|
||||||
|
currentDraggedPluginId.value = item.id
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1034,13 +1196,6 @@ function onDragStartPlugin(evt: any) {
|
|||||||
currentDraggedPluginId.value = item.getAttribute('data-plugin-id')
|
currentDraggedPluginId.value = item.getAttribute('data-plugin-id')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拖拽结束事件
|
|
||||||
function onDragEndPlugin(evt: any) {
|
|
||||||
currentDraggedPluginId.value = ''
|
|
||||||
// 清除拖拽标志
|
|
||||||
isDraggingSortMode.value = false
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1207,66 +1362,37 @@ function onDragEndPlugin(evt: any) {
|
|||||||
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
||||||
|
|
||||||
<!-- 文件夹和插件网格 -->
|
<!-- 文件夹和插件网格 -->
|
||||||
<div v-if="displayedFolders.length > 0 || displayedPlugins.length > 0" class="grid gap-4 grid-plugin-card">
|
<div v-if="(mixedSortList.length > 0 || displayedPlugins.length > 0) && isRefreshed">
|
||||||
<!-- 文件夹卡片 - 使用draggable进行排序 -->
|
<!-- 混合排序列表(文件夹和插件) -->
|
||||||
<draggable
|
|
||||||
v-if="displayedFolders.length > 0 && isRefreshed"
|
|
||||||
v-model="folderOrder"
|
|
||||||
@end="onFolderSortEnd"
|
|
||||||
handle=".cursor-move"
|
|
||||||
item-key="name"
|
|
||||||
tag="div"
|
|
||||||
:component-data="{ style: 'display: contents;' }"
|
|
||||||
:disabled="currentFolder !== ''"
|
|
||||||
group="folders"
|
|
||||||
>
|
|
||||||
<template #item="{ element: folderName }">
|
|
||||||
<div
|
|
||||||
v-if="displayedFolders.find(f => f.name === folderName)"
|
|
||||||
class="drop-zone"
|
|
||||||
@dragover="handleDragOver($event)"
|
|
||||||
@dragenter="handleDragEnter($event)"
|
|
||||||
@dragleave="handleDragLeave($event)"
|
|
||||||
@drop="handleDropToFolder($event, folderName)"
|
|
||||||
>
|
|
||||||
<PluginFolderCard
|
|
||||||
:folder-name="folderName"
|
|
||||||
:plugin-count="displayedFolders.find(f => f.name === folderName)?.pluginCount || 0"
|
|
||||||
:folder-config="displayedFolders.find(f => f.name === folderName)?.config || {}"
|
|
||||||
@open="openFolder"
|
|
||||||
@delete="deleteFolder"
|
|
||||||
@rename="renameFolder"
|
|
||||||
@update-config="updateFolderConfig"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</draggable>
|
|
||||||
|
|
||||||
<!-- 插件卡片 -->
|
|
||||||
<template v-if="!currentFolder">
|
<template v-if="!currentFolder">
|
||||||
<!-- 主列表:使用draggable进行排序 -->
|
<!-- 主列表:使用draggable进行混合排序 -->
|
||||||
<draggable
|
<draggable
|
||||||
v-model="draggableMainPlugins"
|
v-model="mixedSortList"
|
||||||
@end="saveMainPluginOrder"
|
@end="saveMixedSortOrder"
|
||||||
@start="onDragStartPlugin"
|
@start="onDragStartPlugin"
|
||||||
@sort="onDragEndPlugin"
|
|
||||||
handle=".cursor-move"
|
handle=".cursor-move"
|
||||||
item-key="id"
|
item-key="id"
|
||||||
tag="div"
|
tag="div"
|
||||||
:component-data="{ style: 'display: contents;' }"
|
class="grid gap-4 grid-plugin-card"
|
||||||
group="plugins"
|
group="mixed"
|
||||||
>
|
>
|
||||||
<template #item="{ element }">
|
<template #item="{ element }">
|
||||||
<div class="plugin-item-wrapper" :data-plugin-id="element.id">
|
<PluginMixedSortCard
|
||||||
<PluginCard
|
:item="element"
|
||||||
:count="PluginStatistics[element.id || '0']"
|
:plugin-statistics="PluginStatistics"
|
||||||
:plugin="element"
|
:plugin-actions="pluginActions"
|
||||||
:action="pluginActions[element.id || '0']"
|
@open-folder="openFolder"
|
||||||
@remove="refreshData"
|
@delete-folder="deleteFolder"
|
||||||
@save="refreshData"
|
@rename-folder="(oldName, newName) => renameFolder(oldName, newName)"
|
||||||
@action-done="pluginActions[element.id || '0'] = false"
|
@update-folder-config="(folderName, config) => updateFolderConfig(folderName, config)"
|
||||||
/>
|
@refresh-data="refreshData"
|
||||||
</div>
|
@action-done="
|
||||||
|
pluginId => {
|
||||||
|
pluginActions[pluginId] = false
|
||||||
|
}
|
||||||
|
"
|
||||||
|
@drop-to-folder="(event, folderName) => handleDropToFolder(event, folderName)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
</template>
|
</template>
|
||||||
@@ -1280,29 +1406,23 @@ function onDragEndPlugin(evt: any) {
|
|||||||
handle=".cursor-move"
|
handle=".cursor-move"
|
||||||
item-key="id"
|
item-key="id"
|
||||||
tag="div"
|
tag="div"
|
||||||
:component-data="{ style: 'display: contents;' }"
|
class="grid gap-4 grid-plugin-card"
|
||||||
group="plugins"
|
group="plugins"
|
||||||
>
|
>
|
||||||
<template #item="{ element }">
|
<template #item="{ element }">
|
||||||
<div class="plugin-item-wrapper" :data-plugin-id="element.id">
|
<PluginMixedSortCard
|
||||||
<PluginCard
|
:item="{ type: 'plugin', id: element.id, data: element, order: 0 }"
|
||||||
:count="PluginStatistics[element.id || '0']"
|
:plugin-statistics="PluginStatistics"
|
||||||
:plugin="element"
|
:plugin-actions="pluginActions"
|
||||||
:action="pluginActions[element.id || '0']"
|
:show-remove-button="true"
|
||||||
@remove="refreshData"
|
@refresh-data="refreshData"
|
||||||
@save="refreshData"
|
@action-done="
|
||||||
@action-done="pluginActions[element.id || '0'] = false"
|
pluginId => {
|
||||||
/>
|
pluginActions[pluginId] = false
|
||||||
<!-- 移出文件夹按钮 -->
|
}
|
||||||
<VBtn
|
"
|
||||||
icon="mdi-folder-remove"
|
@remove-from-folder="removeFromFolder"
|
||||||
variant="text"
|
/>
|
||||||
color="warning"
|
|
||||||
size="small"
|
|
||||||
class="remove-from-folder-btn"
|
|
||||||
@click="removeFromFolder(element.id || '')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
</template>
|
</template>
|
||||||
@@ -1402,7 +1522,7 @@ function onDragEndPlugin(evt: any) {
|
|||||||
/>
|
/>
|
||||||
</VToolbar>
|
</VToolbar>
|
||||||
<VDialogCloseBtn @click="closeSearchDialog" />
|
<VDialogCloseBtn @click="closeSearchDialog" />
|
||||||
<VList v-if="filterPlugins.length > 0" lines="three">
|
<VList v-if="filterPlugins.length > 0" lines="two">
|
||||||
<VVirtualScroll :items="filterPlugins">
|
<VVirtualScroll :items="filterPlugins">
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<VListItem @click="openPlugin(item)">
|
<VListItem @click="openPlugin(item)">
|
||||||
@@ -1440,6 +1560,7 @@ function onDragEndPlugin(evt: any) {
|
|||||||
</VList>
|
</VList>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
|
||||||
<!-- 安装插件进度框 -->
|
<!-- 安装插件进度框 -->
|
||||||
<VDialog v-if="progressDialog" v-model="progressDialog" :scrim="false" width="25rem">
|
<VDialog v-if="progressDialog" v-model="progressDialog" :scrim="false" width="25rem">
|
||||||
<VCard color="primary">
|
<VCard color="primary">
|
||||||
@@ -1477,35 +1598,5 @@ function onDragEndPlugin(evt: any) {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
// 拖拽相关样式
|
// 样式已移至 PluginMixedSortCard 组件
|
||||||
.drop-zone {
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&.drag-over {
|
|
||||||
transform: scale(1.02);
|
|
||||||
box-shadow: 0 0 20px rgba(33, 150, 243, 0.5);
|
|
||||||
border: 2px dashed #2196f3;
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.plugin-item-wrapper {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.remove-from-folder-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 4px;
|
|
||||||
right: 4px;
|
|
||||||
z-index: 10;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
backdrop-filter: blur(4px);
|
|
||||||
border-radius: 50%;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .remove-from-folder-btn {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import router from '@/router'
|
|||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import { formatFileSize } from '@/@core/utils/formatters'
|
import { formatFileSize } from '@/@core/utils/formatters'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { storageAttributes } from '@/api/constants'
|
|
||||||
|
|
||||||
// i18n
|
// i18n
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|||||||
470
src/views/setting/AccountSettingCache.vue
Normal file
470
src/views/setting/AccountSettingCache.vue
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useToast } from 'vue-toast-notification'
|
||||||
|
import api from '@/api'
|
||||||
|
import type { TorrentCacheData, TorrentCacheItem } from '@/api/types'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useDisplay } from 'vuetify'
|
||||||
|
import { formatFileSize, formatDateDifference } from '@core/utils/formatters'
|
||||||
|
import { useConfirm } from '@/composables/useConfirm'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const display = useDisplay()
|
||||||
|
const appMode = inject('pwaMode') && display.mdAndDown.value
|
||||||
|
|
||||||
|
// 从 provide 中获取全局设置
|
||||||
|
const globalSettings: any = inject('globalSettings')
|
||||||
|
|
||||||
|
// 确认框
|
||||||
|
const createConfirm = useConfirm()
|
||||||
|
|
||||||
|
// 提示框
|
||||||
|
const $toast = useToast()
|
||||||
|
|
||||||
|
// 缓存数据
|
||||||
|
const cacheData = ref<TorrentCacheData>({
|
||||||
|
count: 0,
|
||||||
|
sites: 0,
|
||||||
|
data: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 筛选条件
|
||||||
|
const titleFilter = ref<string | null>(null)
|
||||||
|
const siteFilter = ref<string | null>(null)
|
||||||
|
|
||||||
|
// 获取所有站点选项
|
||||||
|
const siteOptions = computed(() => {
|
||||||
|
const sites = new Set<string>()
|
||||||
|
cacheData.value.data.forEach(item => {
|
||||||
|
if (item.site_name) {
|
||||||
|
sites.add(item.site_name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return Array.from(sites).sort()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 筛选后的数据
|
||||||
|
const filteredData = computed(() => {
|
||||||
|
return cacheData.value.data.filter(item => {
|
||||||
|
const titleMatch = !titleFilter.value || item.title?.toLowerCase().includes(titleFilter.value?.toLowerCase())
|
||||||
|
const siteMatch = !siteFilter.value || item.site_name === siteFilter.value
|
||||||
|
return titleMatch && siteMatch
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 选中的缓存项
|
||||||
|
const selectedItems = ref<string[]>([])
|
||||||
|
|
||||||
|
// 加载状态
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 重新识别对话框
|
||||||
|
const reidentifyDialog = ref(false)
|
||||||
|
const currentReidentifyItem = ref<TorrentCacheItem | null>(null)
|
||||||
|
const tmdbId = ref<number | undefined>()
|
||||||
|
const doubanId = ref<string | undefined>()
|
||||||
|
|
||||||
|
const tableStyle = computed(() => {
|
||||||
|
return appMode ? '' : 'height: calc(100vh - 21rem - env(safe-area-inset-bottom)'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用API加载缓存数据
|
||||||
|
async function loadCacheData() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const res: any = await api.get('torrent/cache')
|
||||||
|
cacheData.value = res.data
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
$toast.error(t('setting.cache.loadFailed'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空所有缓存
|
||||||
|
async function clearAllCache() {
|
||||||
|
const isConfirmed = await createConfirm({
|
||||||
|
type: 'warn',
|
||||||
|
title: t('common.confirm'),
|
||||||
|
content: t('setting.cache.clearConfirm'),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!isConfirmed) return
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
await api.delete('torrent/cache')
|
||||||
|
$toast.success(t('setting.cache.clearSuccess'))
|
||||||
|
await loadCacheData()
|
||||||
|
selectedItems.value = []
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
$toast.error(t('setting.cache.clearFailed'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新缓存
|
||||||
|
async function refreshCache() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const res: any = await api.post('torrent/cache/refresh')
|
||||||
|
$toast.success(res.message || t('setting.cache.refreshSuccess'))
|
||||||
|
await loadCacheData()
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
$toast.error(t('setting.cache.refreshFailed'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除选中的缓存项
|
||||||
|
async function deleteSelectedItems() {
|
||||||
|
if (selectedItems.value.length === 0) {
|
||||||
|
$toast.warning(t('setting.cache.selectDeleteWarning'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const deletePromises = selectedItems.value.map(hash => {
|
||||||
|
const item = cacheData.value.data.find(d => d.hash === hash)
|
||||||
|
if (item) {
|
||||||
|
return api.delete(`torrent/cache/${item.domain}/${hash}`)
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.all(deletePromises)
|
||||||
|
$toast.success(t('setting.cache.deleteSelectedSuccess', { count: selectedItems.value.length }))
|
||||||
|
await loadCacheData()
|
||||||
|
selectedItems.value = []
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
$toast.error(t('setting.cache.deleteSelectedFailed'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除单个缓存项
|
||||||
|
async function deleteSingleItem(item: TorrentCacheItem) {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
await api.delete(`torrent/cache/${item.domain}/${item.hash}`)
|
||||||
|
$toast.success(t('setting.cache.deleteSuccess'))
|
||||||
|
await loadCacheData()
|
||||||
|
// 从选中列表中移除
|
||||||
|
const index = selectedItems.value.indexOf(item.hash)
|
||||||
|
if (index > -1) {
|
||||||
|
selectedItems.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
$toast.error(t('setting.cache.deleteFailed'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开重新识别对话框
|
||||||
|
function openReidentifyDialog(item: TorrentCacheItem) {
|
||||||
|
currentReidentifyItem.value = item
|
||||||
|
tmdbId.value = undefined
|
||||||
|
doubanId.value = undefined
|
||||||
|
reidentifyDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新识别
|
||||||
|
async function performReidentify() {
|
||||||
|
if (!currentReidentifyItem.value) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const params: any = {}
|
||||||
|
if (tmdbId.value) params.tmdbid = tmdbId.value
|
||||||
|
if (doubanId.value) params.doubanid = doubanId.value
|
||||||
|
|
||||||
|
const res: any = await api.post(
|
||||||
|
`torrent/cache/reidentify/${currentReidentifyItem.value.domain}/${currentReidentifyItem.value.hash}`,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
$toast.success(res.message || t('setting.cache.reidentifySuccess'))
|
||||||
|
await loadCacheData()
|
||||||
|
reidentifyDialog.value = false
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
$toast.error(t('setting.cache.reidentifyFailed'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取媒体类型颜色
|
||||||
|
function getMediaTypeColor(type: string): string {
|
||||||
|
switch (type) {
|
||||||
|
case t('setting.cache.mediaType.movie'):
|
||||||
|
return 'primary'
|
||||||
|
case t('setting.cache.mediaType.tv'):
|
||||||
|
return 'success'
|
||||||
|
default:
|
||||||
|
return 'default'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开详情页面
|
||||||
|
function openPageUrl(url: string) {
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadCacheData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VCard>
|
||||||
|
<VCardItem>
|
||||||
|
<VCardTitle>{{ t('setting.cache.title') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ t('setting.cache.subtitle') }}</VCardSubtitle>
|
||||||
|
|
||||||
|
<template #append>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<VBtn icon color="primary" :loading="loading" @click="refreshCache">
|
||||||
|
<VIcon>mdi-refresh</VIcon>
|
||||||
|
<VTooltip activator="parent" location="bottom">{{ t('setting.cache.refresh') }}</VTooltip>
|
||||||
|
</VBtn>
|
||||||
|
|
||||||
|
<VBtn
|
||||||
|
icon
|
||||||
|
color="warning"
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="selectedItems.length === 0"
|
||||||
|
@click="deleteSelectedItems"
|
||||||
|
>
|
||||||
|
<VIcon>mdi-delete-sweep</VIcon>
|
||||||
|
<VTooltip activator="parent" location="bottom"
|
||||||
|
>{{ t('setting.cache.deleteSelected') }} ({{ selectedItems.length }})</VTooltip
|
||||||
|
>
|
||||||
|
</VBtn>
|
||||||
|
|
||||||
|
<VBtn icon color="error" :loading="loading" @click="clearAllCache">
|
||||||
|
<VIcon>mdi-delete-variant</VIcon>
|
||||||
|
<VTooltip activator="parent" location="bottom">{{ t('setting.cache.clearAll') }}</VTooltip>
|
||||||
|
</VBtn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VCardItem>
|
||||||
|
|
||||||
|
<!-- 筛选框 -->
|
||||||
|
<VCardText>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="titleFilter"
|
||||||
|
:label="t('setting.cache.filterByTitle')"
|
||||||
|
prepend-inner-icon="mdi-magnify"
|
||||||
|
clearable
|
||||||
|
density="compact"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="siteFilter"
|
||||||
|
:label="t('setting.cache.filterBySite')"
|
||||||
|
:items="siteOptions"
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
|
clearable
|
||||||
|
density="compact"
|
||||||
|
:placeholder="t('setting.cache.selectSite')"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VCardText>
|
||||||
|
|
||||||
|
<!-- 缓存列表 -->
|
||||||
|
<VDataTable
|
||||||
|
v-model="selectedItems"
|
||||||
|
:headers="[
|
||||||
|
{ title: '', key: 'data-table-select', sortable: false, width: '48px' },
|
||||||
|
{ title: t('setting.cache.poster'), key: 'poster', sortable: false, width: '80px' },
|
||||||
|
{ title: t('setting.cache.torrentTitle'), key: 'title', sortable: true },
|
||||||
|
{ title: t('setting.cache.site'), key: 'site_name', sortable: true, width: '120px' },
|
||||||
|
{ title: t('setting.cache.size'), key: 'size', sortable: true, width: '100px' },
|
||||||
|
{ title: t('setting.cache.publishTime'), key: 'pubdate', sortable: true, width: '150px' },
|
||||||
|
{ title: t('setting.cache.recognitionResult'), key: 'media_info', sortable: false, width: '200px' },
|
||||||
|
{ title: t('setting.cache.actions'), key: 'actions', sortable: false, width: '150px' },
|
||||||
|
]"
|
||||||
|
:items="filteredData"
|
||||||
|
:loading="loading"
|
||||||
|
item-value="hash"
|
||||||
|
show-select
|
||||||
|
hover
|
||||||
|
fixed-header
|
||||||
|
:items-per-page-text="t('common.itemsPerPage')"
|
||||||
|
:no-data-text="t('common.noDataText')"
|
||||||
|
:loading-text="t('common.loadingText')"
|
||||||
|
:style="tableStyle"
|
||||||
|
>
|
||||||
|
<!-- 全选复选框 -->
|
||||||
|
<template #header.data-table-select="{ allSelected, selectAll, someSelected }">
|
||||||
|
<VCheckbox
|
||||||
|
:indeterminate="someSelected && !allSelected"
|
||||||
|
:model-value="allSelected"
|
||||||
|
@update:model-value="(value: boolean | null) => selectAll(value as boolean)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 海报列 -->
|
||||||
|
<template #item.poster="{ item }">
|
||||||
|
<div class="text-center">
|
||||||
|
<VImg
|
||||||
|
v-if="item.poster_path"
|
||||||
|
:src="item.poster_path"
|
||||||
|
:alt="item.media_name || item.title"
|
||||||
|
cover
|
||||||
|
rounded="md"
|
||||||
|
class="w-12 my-1 ms-auto"
|
||||||
|
/>
|
||||||
|
<VIcon v-else size="x-large" color="grey-lighten-1">
|
||||||
|
{{ item.media_type === 'movie' ? 'mdi-movie-open' : 'mdi-television-play' }}
|
||||||
|
</VIcon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 标题列 -->
|
||||||
|
<template #item.title="{ item }">
|
||||||
|
<div class="d-flex flex-column min-w-40">
|
||||||
|
<div class="text-subtitle-2 font-weight-bold">
|
||||||
|
{{ item.title }}
|
||||||
|
</div>
|
||||||
|
<div v-if="item.description" class="text-caption text-grey">
|
||||||
|
{{ item.description }}
|
||||||
|
</div>
|
||||||
|
<div v-if="item.season_episode || item.resource_term" class="text-caption text-primary mt-1">
|
||||||
|
{{ item.season_episode }} {{ item.resource_term }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 大小列 -->
|
||||||
|
<template #item.size="{ item }">
|
||||||
|
{{ formatFileSize(item.size) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 发布时间列 -->
|
||||||
|
<template #item.pubdate="{ item }">
|
||||||
|
{{ formatDateDifference(item.pubdate || '') }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 识别结果列 -->
|
||||||
|
<template #item.media_info="{ item }">
|
||||||
|
<div v-if="item.media_name" class="d-flex flex-column">
|
||||||
|
<div class="text-subtitle-2">
|
||||||
|
{{ item.media_name }}
|
||||||
|
<span v-if="item.media_year" class="text-caption text-grey"> ({{ item.media_year }}) </span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<VChip v-if="item.media_type" :color="getMediaTypeColor(item.media_type)" size="x-small">
|
||||||
|
{{ item.media_type }}
|
||||||
|
</VChip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="text-caption text-grey">
|
||||||
|
{{ t('setting.cache.unrecognized') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 操作列 -->
|
||||||
|
<template #item.actions="{ item }">
|
||||||
|
<div class="d-flex gap-1">
|
||||||
|
<VBtn icon size="small" color="primary" variant="text" @click="openReidentifyDialog(item)">
|
||||||
|
<VIcon size="16">mdi-text-recognition</VIcon>
|
||||||
|
</VBtn>
|
||||||
|
|
||||||
|
<VBtn icon size="small" color="error" variant="text" @click="deleteSingleItem(item)">
|
||||||
|
<VIcon size="16">mdi-delete</VIcon>
|
||||||
|
</VBtn>
|
||||||
|
|
||||||
|
<VBtn
|
||||||
|
v-if="item.page_url"
|
||||||
|
icon
|
||||||
|
size="small"
|
||||||
|
color="info"
|
||||||
|
variant="text"
|
||||||
|
@click="openPageUrl(item.page_url || '')"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<VIcon size="16">mdi-open-in-new</VIcon>
|
||||||
|
</VBtn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<template #no-data>
|
||||||
|
<div class="text-center pa-4">
|
||||||
|
<VIcon size="64" class="mb-4"> mdi-database-off </VIcon>
|
||||||
|
<div class="text-body-2 text-grey">
|
||||||
|
{{ t('setting.cache.noData') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VDataTable>
|
||||||
|
</VCard>
|
||||||
|
|
||||||
|
<!-- 重新识别对话框 -->
|
||||||
|
<VDialog v-model="reidentifyDialog" scrollable max-width="35rem">
|
||||||
|
<VCard>
|
||||||
|
<VCardItem class="py-2">
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon>mdi-text-recognition</VIcon>
|
||||||
|
</template>
|
||||||
|
<VCardTitle>{{ t('setting.cache.reidentifyDialog.title') }}</VCardTitle>
|
||||||
|
<VCardSubtitle>{{ currentReidentifyItem?.title }}</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
|
<VDialogCloseBtn @click="reidentifyDialog = false" />
|
||||||
|
<VDivider />
|
||||||
|
<VCardText>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12">
|
||||||
|
<VTextField
|
||||||
|
v-if="globalSettings.RECOGNIZE_SOURCE === 'themoviedb'"
|
||||||
|
v-model="tmdbId"
|
||||||
|
:label="t('setting.cache.reidentifyDialog.tmdbId')"
|
||||||
|
:hint="t('setting.cache.reidentifyDialog.tmdbIdHint')"
|
||||||
|
clearable
|
||||||
|
prepend-inner-icon="mdi-id-card"
|
||||||
|
persistent-hint
|
||||||
|
/>
|
||||||
|
<VTextField
|
||||||
|
v-else
|
||||||
|
v-model="doubanId"
|
||||||
|
:label="t('setting.cache.reidentifyDialog.doubanId')"
|
||||||
|
:hint="t('setting.cache.reidentifyDialog.doubanIdHint')"
|
||||||
|
clearable
|
||||||
|
prepend-inner-icon="mdi-id-card"
|
||||||
|
persistent-hint
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VAlert type="info" variant="tonal" class="mt-4">
|
||||||
|
{{ t('setting.cache.reidentifyDialog.autoHint') }}
|
||||||
|
</VAlert>
|
||||||
|
</VCardText>
|
||||||
|
|
||||||
|
<VCardActions>
|
||||||
|
<VSpacer />
|
||||||
|
<VBtn color="primary" :loading="loading" prepend-icon="mdi-check" @click="performReidentify">
|
||||||
|
{{ t('setting.cache.reidentifyDialog.confirm') }}
|
||||||
|
</VBtn>
|
||||||
|
</VCardActions>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
|
</template>
|
||||||
@@ -239,7 +239,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" class="me-2" @click="saveStorages"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" class="me-2" @click="saveStorages" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
<VBtn color="success" variant="tonal" @click="addStorage">
|
<VBtn color="success" variant="tonal" @click="addStorage">
|
||||||
<VIcon icon="mdi-plus" />
|
<VIcon icon="mdi-plus" />
|
||||||
</VBtn>
|
</VBtn>
|
||||||
@@ -279,7 +281,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveDirectories"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveDirectories" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
<VBtn color="success" variant="tonal" @click="addDirectory">
|
<VBtn color="success" variant="tonal" @click="addDirectory">
|
||||||
<VIcon icon="mdi-plus" />
|
<VIcon icon="mdi-plus" />
|
||||||
</VBtn>
|
</VBtn>
|
||||||
@@ -305,6 +309,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.directory.scrapSource')"
|
:label="t('setting.directory.scrapSource')"
|
||||||
:hint="t('setting.directory.scrapSourceHint')"
|
:hint="t('setting.directory.scrapSourceHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-database"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -315,6 +320,7 @@ onMounted(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
clearable
|
clearable
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-movie-open"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -325,6 +331,7 @@ onMounted(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
clearable
|
clearable
|
||||||
active
|
active
|
||||||
|
prepend-inner-icon="mdi-television"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -332,7 +339,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveSystemSettings(SystemSettings.Basic)"> {{ t('common.save') }}</VBtn>
|
<VBtn type="submit" @click="saveSystemSettings(SystemSettings.Basic)" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -293,7 +293,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn mtype="submit" @click="saveNotificationSetting"> {{ t('common.save') }} </VBtn>
|
<VBtn mtype="submit" @click="saveNotificationSetting" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
<VBtn color="success" variant="tonal">
|
<VBtn color="success" variant="tonal">
|
||||||
<VIcon icon="mdi-plus" />
|
<VIcon icon="mdi-plus" />
|
||||||
<VMenu :activator="'parent'" :close-on-content-click="true">
|
<VMenu :activator="'parent'" :close-on-content-click="true">
|
||||||
@@ -401,7 +403,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveNotificationSwitchs"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveNotificationSwitchs" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -418,17 +422,29 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="6">
|
<VCol cols="6">
|
||||||
<VTextField v-model="notificationTime.start" :label="t('setting.notification.startTime')" type="time" />
|
<VTextField
|
||||||
|
v-model="notificationTime.start"
|
||||||
|
:label="t('setting.notification.startTime')"
|
||||||
|
type="time"
|
||||||
|
prepend-inner-icon="mdi-clock-start"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="6">
|
<VCol cols="6">
|
||||||
<VTextField v-model="notificationTime.end" :label="t('setting.notification.endTime')" type="time" />
|
<VTextField
|
||||||
|
v-model="notificationTime.end"
|
||||||
|
:label="t('setting.notification.endTime')"
|
||||||
|
type="time"
|
||||||
|
prepend-inner-icon="mdi-clock-end"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveNotificationTime"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveNotificationTime" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -445,11 +461,16 @@ onMounted(() => {
|
|||||||
<!-- 模板编辑器对话框 -->
|
<!-- 模板编辑器对话框 -->
|
||||||
<VDialog v-model="editorVisible" v-if="editorVisible" max-width="50rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog v-model="editorVisible" v-if="editorVisible" max-width="50rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard>
|
<VCard>
|
||||||
<VCardItem>
|
<VCardItem class="py-2">
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-code-json" class="me-2" />
|
||||||
|
</template>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
{{ templateTypes.find(t => t.type === currentTemplate)?.label }}
|
|
||||||
{{ t('setting.notification.templateConfigTitle') }}
|
{{ t('setting.notification.templateConfigTitle') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
|
<VCardSubtitle>
|
||||||
|
{{ templateTypes.find(t => t.type === currentTemplate)?.label }}
|
||||||
|
</VCardSubtitle>
|
||||||
<VDialogCloseBtn @click="editorVisible = false" />
|
<VDialogCloseBtn @click="editorVisible = false" />
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VCardText class="py-0">
|
<VCardText class="py-0">
|
||||||
|
|||||||
@@ -401,7 +401,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" class="me-2" @click="saveCustomRules"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" class="me-2" @click="saveCustomRules" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
<VBtnGroup density="comfortable">
|
<VBtnGroup density="comfortable">
|
||||||
<VBtn color="success" variant="tonal" @click="addCustomRule">
|
<VBtn color="success" variant="tonal" @click="addCustomRule">
|
||||||
<VIcon icon="mdi-plus" />
|
<VIcon icon="mdi-plus" />
|
||||||
@@ -452,7 +454,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" class="me-2" @click="saveFilterRuleGroups"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" class="me-2" @click="saveFilterRuleGroups" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
<VBtnGroup density="comfortable">
|
<VBtnGroup density="comfortable">
|
||||||
<VBtn color="success" variant="tonal" @click="addFilterRuleGroup">
|
<VBtn color="success" variant="tonal" @click="addFilterRuleGroup">
|
||||||
<VIcon icon="mdi-plus" />
|
<VIcon icon="mdi-plus" />
|
||||||
@@ -501,6 +505,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.rule.currentPriorityRules')"
|
:label="t('setting.rule.currentPriorityRules')"
|
||||||
:hint="t('setting.rule.currentPriorityRulesHint')"
|
:hint="t('setting.rule.currentPriorityRulesHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-priority-high"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -509,7 +514,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveTorrentPriority"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveTorrentPriority" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.search.mediaSource')"
|
:label="t('setting.search.mediaSource')"
|
||||||
:hint="t('setting.search.mediaSourceHint')"
|
:hint="t('setting.search.mediaSourceHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-database-search"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -217,6 +218,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.search.filterRuleGroup')"
|
:label="t('setting.search.filterRuleGroup')"
|
||||||
:hint="t('setting.search.filterRuleGroupHint')"
|
:hint="t('setting.search.filterRuleGroupHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-filter"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -228,6 +230,7 @@ onMounted(() => {
|
|||||||
placeholder="MOVIEPILOT"
|
placeholder="MOVIEPILOT"
|
||||||
:hint="t('setting.search.downloadLabelHint')"
|
:hint="t('setting.search.downloadLabelHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-tag"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -237,6 +240,7 @@ onMounted(() => {
|
|||||||
:placeholder="t('setting.search.downloadUserPlaceholder')"
|
:placeholder="t('setting.search.downloadUserPlaceholder')"
|
||||||
:hint="t('setting.search.downloadUserHint')"
|
:hint="t('setting.search.downloadUserHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -260,7 +264,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveSearchSetting"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveSearchSetting" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -291,7 +297,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveSelectedSites"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveSelectedSites" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ onMounted(() => {
|
|||||||
:disabled="siteSetting.CookieCloud.COOKIECLOUD_ENABLE_LOCAL"
|
:disabled="siteSetting.CookieCloud.COOKIECLOUD_ENABLE_LOCAL"
|
||||||
:hint="t('setting.site.serviceAddressHint')"
|
:hint="t('setting.site.serviceAddressHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-server"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -169,6 +170,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.site.userKey')"
|
:label="t('setting.site.userKey')"
|
||||||
:hint="t('setting.site.userKeyHint')"
|
:hint="t('setting.site.userKeyHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -180,6 +182,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.site.e2ePassword')"
|
:label="t('setting.site.e2ePassword')"
|
||||||
:hint="t('setting.site.e2ePasswordHint')"
|
:hint="t('setting.site.e2ePasswordHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -189,6 +192,7 @@ onMounted(() => {
|
|||||||
:items="CookieCloudIntervalItems"
|
:items="CookieCloudIntervalItems"
|
||||||
:hint="t('setting.site.autoSyncIntervalHint')"
|
:hint="t('setting.site.autoSyncIntervalHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-timer"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -198,6 +202,7 @@ onMounted(() => {
|
|||||||
:placeholder="t('setting.site.syncBlacklistPlaceholder')"
|
:placeholder="t('setting.site.syncBlacklistPlaceholder')"
|
||||||
:hint="t('setting.site.syncBlacklistHint')"
|
:hint="t('setting.site.syncBlacklistHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-block-helper"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -206,6 +211,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.site.userAgent')"
|
:label="t('setting.site.userAgent')"
|
||||||
:hint="t('setting.site.userAgentHint')"
|
:hint="t('setting.site.userAgentHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -214,7 +220,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveSiteSetting(siteSetting.CookieCloud)"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveSiteSetting(siteSetting.CookieCloud)" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -234,6 +242,7 @@ onMounted(() => {
|
|||||||
:items="SiteDataRefreshIntervalItems"
|
:items="SiteDataRefreshIntervalItems"
|
||||||
:hint="t('setting.site.siteDataRefreshIntervalHint')"
|
:hint="t('setting.site.siteDataRefreshIntervalHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-refresh"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -252,7 +261,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveSiteSetting(siteSetting.Site)"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveSiteSetting(siteSetting.Site)" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.subscribe.mode')"
|
:label="t('setting.subscribe.mode')"
|
||||||
:hint="t('setting.subscribe.modeHint')"
|
:hint="t('setting.subscribe.modeHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-cog"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -226,6 +227,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.subscribe.rssInterval')"
|
:label="t('setting.subscribe.rssInterval')"
|
||||||
:hint="t('setting.subscribe.rssIntervalHint')"
|
:hint="t('setting.subscribe.rssIntervalHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-timer"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -238,6 +240,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.subscribe.filterRuleGroup')"
|
:label="t('setting.subscribe.filterRuleGroup')"
|
||||||
:hint="t('setting.subscribe.filterRuleGroupHint')"
|
:hint="t('setting.subscribe.filterRuleGroupHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-filter"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -250,6 +253,7 @@ onMounted(() => {
|
|||||||
:label="t('setting.subscribe.bestVersionRuleGroup')"
|
:label="t('setting.subscribe.bestVersionRuleGroup')"
|
||||||
:hint="t('setting.subscribe.bestVersionRuleGroupHint')"
|
:hint="t('setting.subscribe.bestVersionRuleGroupHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-star"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -276,7 +280,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveSubscribeSetting"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveSubscribeSetting" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -307,7 +313,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveSelectedRssSites"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveSelectedRssSites" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ const SystemSettings = ref<any>({
|
|||||||
PLUGIN_STATISTIC_SHARE: true,
|
PLUGIN_STATISTIC_SHARE: true,
|
||||||
BIG_MEMORY_MODE: false,
|
BIG_MEMORY_MODE: false,
|
||||||
DB_WAL_ENABLE: false,
|
DB_WAL_ENABLE: false,
|
||||||
|
AUTO_UPDATE_RESOURCE: true,
|
||||||
|
MOVIEPILOT_AUTO_UPDATE: false,
|
||||||
// 媒体
|
// 媒体
|
||||||
TMDB_API_DOMAIN: null,
|
TMDB_API_DOMAIN: null,
|
||||||
TMDB_IMAGE_DOMAIN: null,
|
TMDB_IMAGE_DOMAIN: null,
|
||||||
@@ -385,6 +387,16 @@ function onMediaServerChange(mediaserver: MediaServerConf, name: string) {
|
|||||||
if (index !== -1) mediaServers.value[index] = mediaserver
|
if (index !== -1) mediaServers.value[index] = mediaserver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加计算属性
|
||||||
|
const moviePilotAutoUpdate = computed({
|
||||||
|
get: () => {
|
||||||
|
return ['release', 'dev'].includes(SystemSettings.value.Advanced.MOVIEPILOT_AUTO_UPDATE)
|
||||||
|
},
|
||||||
|
set: val => {
|
||||||
|
SystemSettings.value.Advanced.MOVIEPILOT_AUTO_UPDATE = val ? 'release' : 'false'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadDownloaderSetting()
|
loadDownloaderSetting()
|
||||||
@@ -426,6 +438,7 @@ onDeactivated(() => {
|
|||||||
:hint="t('setting.system.appDomainHint')"
|
:hint="t('setting.system.appDomainHint')"
|
||||||
placeholder="http://localhost:3000"
|
placeholder="http://localhost:3000"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
@@ -438,6 +451,7 @@ onDeactivated(() => {
|
|||||||
:hint="t('setting.system.wallpaperHint')"
|
:hint="t('setting.system.wallpaperHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:items="wallpaperItems"
|
:items="wallpaperItems"
|
||||||
|
prepend-inner-icon="mdi-image"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
@@ -449,6 +463,7 @@ onDeactivated(() => {
|
|||||||
:placeholder="t('setting.system.customizeWallpaperApi')"
|
:placeholder="t('setting.system.customizeWallpaperApi')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:rules="[v => !!v || t('setting.system.customizeWallpaperApiRequired')]"
|
:rules="[v => !!v || t('setting.system.customizeWallpaperApiRequired')]"
|
||||||
|
prepend-inner-icon="mdi-api"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -463,6 +478,7 @@ onDeactivated(() => {
|
|||||||
{ title: 'TheMovieDb', value: 'themoviedb' },
|
{ title: 'TheMovieDb', value: 'themoviedb' },
|
||||||
{ title: '豆瓣', value: 'douban' },
|
{ title: '豆瓣', value: 'douban' },
|
||||||
]"
|
]"
|
||||||
|
prepend-inner-icon="mdi-database"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -479,6 +495,7 @@ onDeactivated(() => {
|
|||||||
(v: any) => !isNaN(v) || t('setting.system.numbersOnly'),
|
(v: any) => !isNaN(v) || t('setting.system.numbersOnly'),
|
||||||
(v: any) => v >= 1 || t('setting.system.minInterval'),
|
(v: any) => v >= 1 || t('setting.system.minInterval'),
|
||||||
]"
|
]"
|
||||||
|
prepend-inner-icon="mdi-sync"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -488,10 +505,11 @@ onDeactivated(() => {
|
|||||||
:hint="t('setting.system.apiTokenHint')"
|
:hint="t('setting.system.apiTokenHint')"
|
||||||
:placeholder="t('setting.system.apiTokenMinChars')"
|
:placeholder="t('setting.system.apiTokenMinChars')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
prependInnerIcon="mdi-reload"
|
prepend-inner-icon="mdi-key"
|
||||||
:appendInnerIcon="SystemSettings.Basic.API_TOKEN ? 'mdi-content-copy' : ''"
|
:append-inner-icon="SystemSettings.Basic.API_TOKEN ? 'mdi-content-copy' : 'mdi-reload'"
|
||||||
@click:prependInner="createRandomString"
|
@click:append-inner="
|
||||||
@click:appendInner="copyValue(SystemSettings.Basic.API_TOKEN)"
|
SystemSettings.Basic.API_TOKEN ? copyValue(SystemSettings.Basic.API_TOKEN) : createRandomString()
|
||||||
|
"
|
||||||
:rules="[
|
:rules="[
|
||||||
(v: string) => !!v || t('setting.system.apiTokenRequired'),
|
(v: string) => !!v || t('setting.system.apiTokenRequired'),
|
||||||
(v: string) => v.length >= 16 || t('setting.system.apiTokenLength'),
|
(v: string) => v.length >= 16 || t('setting.system.apiTokenLength'),
|
||||||
@@ -505,6 +523,7 @@ onDeactivated(() => {
|
|||||||
:placeholder="t('setting.system.githubTokenFormat')"
|
:placeholder="t('setting.system.githubTokenFormat')"
|
||||||
:hint="t('setting.system.githubTokenHint')"
|
:hint="t('setting.system.githubTokenHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-github"
|
||||||
>
|
>
|
||||||
</VTextField>
|
</VTextField>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -515,6 +534,7 @@ onDeactivated(() => {
|
|||||||
placeholder="https://movie-pilot.org"
|
placeholder="https://movie-pilot.org"
|
||||||
:hint="t('setting.system.ocrHostHint')"
|
:hint="t('setting.system.ocrHostHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-text-recognition"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -523,7 +543,9 @@ onDeactivated(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveBasicSettings"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveBasicSettings" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
<VBtn
|
<VBtn
|
||||||
color="error"
|
color="error"
|
||||||
@@ -568,7 +590,9 @@ onDeactivated(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveDownloaderSetting"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveDownloaderSetting" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
<VBtn color="success" variant="tonal">
|
<VBtn color="success" variant="tonal">
|
||||||
<VIcon icon="mdi-plus" />
|
<VIcon icon="mdi-plus" />
|
||||||
<VMenu activator="parent" close-on-content-click>
|
<VMenu activator="parent" close-on-content-click>
|
||||||
@@ -616,7 +640,9 @@ onDeactivated(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveMediaServerSetting"> {{ t('common.save') }} </VBtn>
|
<VBtn type="submit" @click="saveMediaServerSetting" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
<VBtn color="success" variant="tonal">
|
<VBtn color="success" variant="tonal">
|
||||||
<VIcon icon="mdi-plus" />
|
<VIcon icon="mdi-plus" />
|
||||||
<VMenu activator="parent" close-on-content-click>
|
<VMenu activator="parent" close-on-content-click>
|
||||||
@@ -721,6 +747,22 @@ onDeactivated(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSwitch
|
||||||
|
v-model="moviePilotAutoUpdate"
|
||||||
|
:label="t('setting.system.moviePilotAutoUpdate')"
|
||||||
|
:hint="t('setting.system.moviePilotAutoUpdateHint')"
|
||||||
|
persistent-hint
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSwitch
|
||||||
|
v-model="SystemSettings.Advanced.AUTO_UPDATE_RESOURCE"
|
||||||
|
:label="t('setting.system.autoUpdateResource')"
|
||||||
|
:hint="t('setting.system.autoUpdateResourceHint')"
|
||||||
|
persistent-hint
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</div>
|
</div>
|
||||||
</VWindowItem>
|
</VWindowItem>
|
||||||
@@ -736,6 +778,7 @@ onDeactivated(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
:items="['api.themoviedb.org', 'api.tmdb.org']"
|
:items="['api.themoviedb.org', 'api.tmdb.org']"
|
||||||
:rules="[(v: string) => !!v || t('setting.system.tmdbApiDomainRequired')]"
|
:rules="[(v: string) => !!v || t('setting.system.tmdbApiDomainRequired')]"
|
||||||
|
prepend-inner-icon="mdi-api"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -747,6 +790,7 @@ onDeactivated(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
:items="['image.tmdb.org', 'static-mdb.v.geilijiasu.com']"
|
:items="['image.tmdb.org', 'static-mdb.v.geilijiasu.com']"
|
||||||
:rules="[(v: string) => !!v || t('setting.system.tmdbImageDomainRequired')]"
|
:rules="[(v: string) => !!v || t('setting.system.tmdbImageDomainRequired')]"
|
||||||
|
prepend-inner-icon="mdi-image"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -757,6 +801,7 @@ onDeactivated(() => {
|
|||||||
:hint="t('setting.system.tmdbLocaleHint')"
|
:hint="t('setting.system.tmdbLocaleHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:items="tmdbLanguageItems"
|
:items="tmdbLanguageItems"
|
||||||
|
prepend-inner-icon="mdi-translate"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -772,6 +817,7 @@ onDeactivated(() => {
|
|||||||
(v: any) => v === 0 || !!v || t('setting.system.metaCacheExpireRequired'),
|
(v: any) => v === 0 || !!v || t('setting.system.metaCacheExpireRequired'),
|
||||||
(v: any) => v >= 0 || t('setting.system.metaCacheExpireMin'),
|
(v: any) => v >= 0 || t('setting.system.metaCacheExpireMin'),
|
||||||
]"
|
]"
|
||||||
|
prepend-inner-icon="mdi-timer"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -806,6 +852,16 @@ onDeactivated(() => {
|
|||||||
<VWindowItem value="network">
|
<VWindowItem value="network">
|
||||||
<div>
|
<div>
|
||||||
<VRow>
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="SystemSettings.Advanced.PROXY_HOST"
|
||||||
|
:label="t('setting.system.proxyHost')"
|
||||||
|
placeholder="http://127.0.0.1:7890"
|
||||||
|
:hint="t('setting.system.proxyHostHint')"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-server-network"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VCombobox
|
<VCombobox
|
||||||
v-model="githubProxyDisplay"
|
v-model="githubProxyDisplay"
|
||||||
@@ -815,9 +871,10 @@ onDeactivated(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
:items="githubMirrorsItems"
|
:items="githubMirrorsItems"
|
||||||
clearable
|
clearable
|
||||||
|
prepend-inner-icon="mdi-github"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12">
|
||||||
<VCombobox
|
<VCombobox
|
||||||
v-model="pipProxyDisplay"
|
v-model="pipProxyDisplay"
|
||||||
:label="t('setting.system.pipProxy')"
|
:label="t('setting.system.pipProxy')"
|
||||||
@@ -826,6 +883,7 @@ onDeactivated(() => {
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
:items="pipMirrorsItems"
|
:items="pipMirrorsItems"
|
||||||
clearable
|
clearable
|
||||||
|
prepend-inner-icon="mdi-package"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -845,6 +903,7 @@ onDeactivated(() => {
|
|||||||
:placeholder="t('setting.system.dohResolversPlaceholder')"
|
:placeholder="t('setting.system.dohResolversPlaceholder')"
|
||||||
:hint="t('setting.system.dohResolversHint')"
|
:hint="t('setting.system.dohResolversHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-dns"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" v-show="SystemSettings.Advanced.DOH_ENABLE">
|
<VCol cols="12" v-show="SystemSettings.Advanced.DOH_ENABLE">
|
||||||
@@ -854,6 +913,7 @@ onDeactivated(() => {
|
|||||||
:placeholder="t('setting.system.dohDomainsPlaceholder')"
|
:placeholder="t('setting.system.dohDomainsPlaceholder')"
|
||||||
:hint="t('setting.system.dohDomainsHint')"
|
:hint="t('setting.system.dohDomainsHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-domain"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -885,6 +945,7 @@ onDeactivated(() => {
|
|||||||
:placeholder="t('setting.system.securityImageDomainAdd')"
|
:placeholder="t('setting.system.securityImageDomainAdd')"
|
||||||
hide-details
|
hide-details
|
||||||
density="compact"
|
density="compact"
|
||||||
|
prepend-inner-icon="mdi-shield-check"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<VBtn icon color="primary" @click="addSecurityDomain" :disabled="!newSecurityDomain">
|
<VBtn icon color="primary" @click="addSecurityDomain" :disabled="!newSecurityDomain">
|
||||||
@@ -918,6 +979,7 @@ onDeactivated(() => {
|
|||||||
:hint="t('setting.system.logLevelHint')"
|
:hint="t('setting.system.logLevelHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:items="logLevelItems"
|
:items="logLevelItems"
|
||||||
|
prepend-inner-icon="mdi-format-list-bulleted"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -930,6 +992,7 @@ onDeactivated(() => {
|
|||||||
type="number"
|
type="number"
|
||||||
:suffix="t('setting.system.mb')"
|
:suffix="t('setting.system.mb')"
|
||||||
:rules="[(v: any) => v === 0 || !!v || t('setting.system.logMaxFileSizeRequired'), (v: any) => v >= 1 || t('setting.system.logMaxFileSizeMin')]"
|
:rules="[(v: any) => v === 0 || !!v || t('setting.system.logMaxFileSizeRequired'), (v: any) => v >= 1 || t('setting.system.logMaxFileSizeMin')]"
|
||||||
|
prepend-inner-icon="mdi-file-document"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -941,6 +1004,7 @@ onDeactivated(() => {
|
|||||||
min="1"
|
min="1"
|
||||||
type="number"
|
type="number"
|
||||||
:rules="[(v: any) => v === 0 || !!v || t('setting.system.logBackupCountRequired'), (v: any) => v >= 1 || t('setting.system.logBackupCountMin')]"
|
:rules="[(v: any) => v === 0 || !!v || t('setting.system.logBackupCountRequired'), (v: any) => v >= 1 || t('setting.system.logBackupCountMin')]"
|
||||||
|
prepend-inner-icon="mdi-backup-restore"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
@@ -949,6 +1013,7 @@ onDeactivated(() => {
|
|||||||
:label="t('setting.system.logFileFormat')"
|
:label="t('setting.system.logFileFormat')"
|
||||||
:hint="t('setting.system.logFileFormatHint')"
|
:hint="t('setting.system.logFileFormatHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-format-text"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ onMounted(() => {
|
|||||||
:placeholder="t('setting.words.identifiersPlaceholder')"
|
:placeholder="t('setting.words.identifiersPlaceholder')"
|
||||||
:hint="t('setting.words.identifiersHint')"
|
:hint="t('setting.words.identifiersHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-tag-text"
|
||||||
/>
|
/>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
@@ -153,7 +154,9 @@ onMounted(() => {
|
|||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveCustomIdentifiers">{{ t('common.save') }}</VBtn>
|
<VBtn type="submit" @click="saveCustomIdentifiers" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -173,12 +176,15 @@ onMounted(() => {
|
|||||||
:placeholder="t('setting.words.releaseGroupsPlaceholder')"
|
:placeholder="t('setting.words.releaseGroupsPlaceholder')"
|
||||||
:hint="t('setting.words.releaseGroupsHint')"
|
:hint="t('setting.words.releaseGroupsHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account-group"
|
||||||
/>
|
/>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveCustomReleaseGroups">{{ t('common.save') }}</VBtn>
|
<VBtn type="submit" @click="saveCustomReleaseGroups" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -198,12 +204,15 @@ onMounted(() => {
|
|||||||
:placeholder="t('setting.words.customizationPlaceholder')"
|
:placeholder="t('setting.words.customizationPlaceholder')"
|
||||||
:hint="t('setting.words.customizationHint')"
|
:hint="t('setting.words.customizationHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-code-braces"
|
||||||
/>
|
/>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveCustomization">{{ t('common.save') }}</VBtn>
|
<VBtn type="submit" @click="saveCustomization" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -223,12 +232,15 @@ onMounted(() => {
|
|||||||
:placeholder="t('setting.words.excludeWordsPlaceholder')"
|
:placeholder="t('setting.words.excludeWordsPlaceholder')"
|
||||||
:hint="t('setting.words.excludeWordsHint')"
|
:hint="t('setting.words.excludeWordsHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-block-helper"
|
||||||
/>
|
/>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<VBtn type="submit" @click="saveTransferExcludeWords">{{ t('common.save') }}</VBtn>
|
<VBtn type="submit" @click="saveTransferExcludeWords" prepend-icon="mdi-content-save">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 定义输入变量
|
||||||
|
const props = defineProps<{
|
||||||
|
logfile: string
|
||||||
|
}>()
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
@@ -33,7 +38,12 @@ function getLogColor(level: string): string {
|
|||||||
|
|
||||||
// SSE持续获取日志
|
// SSE持续获取日志
|
||||||
function startSSELogging() {
|
function startSSELogging() {
|
||||||
eventSource = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/logging`)
|
console.log(props.logfile)
|
||||||
|
eventSource = new EventSource(
|
||||||
|
`${import.meta.env.VITE_API_BASE_URL}system/logging?logfile=${
|
||||||
|
encodeURIComponent(props.logfile) ?? 'moviepilot.log'
|
||||||
|
}`,
|
||||||
|
)
|
||||||
const buffer: string[] = []
|
const buffer: string[] = []
|
||||||
let timeoutId: number | null = null
|
let timeoutId: number | null = null
|
||||||
|
|
||||||
|
|||||||
@@ -54,10 +54,21 @@ async function nameTest() {
|
|||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<VRow class="pt-2">
|
<VRow class="pt-2">
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VTextField v-model="nameTestForm.title" :label="t('nameTest.title')" :rules="[requiredValidator]" />
|
<VTextField
|
||||||
|
v-model="nameTestForm.title"
|
||||||
|
:label="t('nameTest.title')"
|
||||||
|
:rules="[requiredValidator]"
|
||||||
|
prepend-inner-icon="mdi-movie-open"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VTextarea v-model="nameTestForm.subtitle" :label="t('nameTest.subtitle')" rows="2" auto-grow />
|
<VTextarea
|
||||||
|
v-model="nameTestForm.subtitle"
|
||||||
|
:label="t('nameTest.subtitle')"
|
||||||
|
rows="2"
|
||||||
|
auto-grow
|
||||||
|
prepend-inner-icon="mdi-subtitles"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
<VRow>
|
<VRow>
|
||||||
|
|||||||
@@ -80,13 +80,29 @@ onMounted(() => {
|
|||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<VRow class="pt-2">
|
<VRow class="pt-2">
|
||||||
<VCol cols="12" md="8">
|
<VCol cols="12" md="8">
|
||||||
<VTextField v-model="ruleTestForm.title" :label="t('ruleTest.title')" :rules="[requiredValidator]" />
|
<VTextField
|
||||||
|
v-model="ruleTestForm.title"
|
||||||
|
:label="t('ruleTest.title')"
|
||||||
|
:rules="[requiredValidator]"
|
||||||
|
prepend-inner-icon="mdi-movie-open"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4">
|
<VCol cols="12" md="4">
|
||||||
<VSelect v-model="ruleTestForm.rulegroup" :label="t('ruleTest.ruleGroup')" :items="filterRuleGroupItems" />
|
<VSelect
|
||||||
|
v-model="ruleTestForm.rulegroup"
|
||||||
|
:label="t('ruleTest.ruleGroup')"
|
||||||
|
:items="filterRuleGroupItems"
|
||||||
|
prepend-inner-icon="mdi-filter"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VTextarea v-model="ruleTestForm.subtitle" :label="t('ruleTest.subtitle')" rows="2" auto-grow />
|
<VTextarea
|
||||||
|
v-model="ruleTestForm.subtitle"
|
||||||
|
:label="t('ruleTest.subtitle')"
|
||||||
|
rows="2"
|
||||||
|
auto-grow
|
||||||
|
prepend-inner-icon="mdi-subtitles"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
<VRow>
|
<VRow>
|
||||||
|
|||||||
@@ -322,7 +322,13 @@ watch(
|
|||||||
<VForm class="mt-6">
|
<VForm class="mt-6">
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField v-model="currentUserName" density="comfortable" readonly :label="t('user.username')" />
|
<VTextField
|
||||||
|
v-model="currentUserName"
|
||||||
|
density="comfortable"
|
||||||
|
readonly
|
||||||
|
:label="t('user.username')"
|
||||||
|
prepend-inner-icon="mdi-account"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField
|
<VTextField
|
||||||
@@ -331,6 +337,7 @@ watch(
|
|||||||
clearable
|
clearable
|
||||||
:label="t('user.email')"
|
:label="t('user.email')"
|
||||||
type="email"
|
type="email"
|
||||||
|
prepend-inner-icon="mdi-email"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -342,6 +349,7 @@ watch(
|
|||||||
clearable
|
clearable
|
||||||
:label="t('user.password')"
|
:label="t('user.password')"
|
||||||
autocomplete=""
|
autocomplete=""
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -354,6 +362,7 @@ watch(
|
|||||||
:append-inner-icon="isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
:append-inner-icon="isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
||||||
clearable
|
clearable
|
||||||
:label="t('user.confirmPassword')"
|
:label="t('user.confirmPassword')"
|
||||||
|
prepend-inner-icon="mdi-lock-check"
|
||||||
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
|
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -364,6 +373,7 @@ watch(
|
|||||||
clearable
|
clearable
|
||||||
:label="t('profile.nickname')"
|
:label="t('profile.nickname')"
|
||||||
:placeholder="t('profile.nicknamePlaceholder')"
|
:placeholder="t('profile.nicknamePlaceholder')"
|
||||||
|
prepend-inner-icon="mdi-card-account-details"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -379,6 +389,7 @@ watch(
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('profile.wechatUser')"
|
:label="t('profile.wechatUser')"
|
||||||
|
prepend-inner-icon="mdi-wechat"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -387,6 +398,7 @@ watch(
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('profile.telegramUser')"
|
:label="t('profile.telegramUser')"
|
||||||
|
prepend-inner-icon="mdi-send"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -395,6 +407,7 @@ watch(
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('profile.slackUser')"
|
:label="t('profile.slackUser')"
|
||||||
|
prepend-inner-icon="mdi-slack"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -403,6 +416,7 @@ watch(
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('profile.vocechatUser')"
|
:label="t('profile.vocechatUser')"
|
||||||
|
prepend-inner-icon="mdi-chat"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -411,6 +425,7 @@ watch(
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('profile.synologychatUser')"
|
:label="t('profile.synologychatUser')"
|
||||||
|
prepend-inner-icon="mdi-message"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -419,13 +434,14 @@ watch(
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
:label="t('profile.doubanUser')"
|
:label="t('profile.doubanUser')"
|
||||||
|
prepend-inner-icon="mdi-movie"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
<VRow>
|
<VRow>
|
||||||
<!-- 👉 Form Actions -->
|
<!-- 👉 Form Actions -->
|
||||||
<VCol cols="12" class="d-flex flex-wrap gap-4">
|
<VCol cols="12" class="d-flex flex-wrap gap-4">
|
||||||
<VBtn @click="saveAccountInfo" :disabled="isSaving">
|
<VBtn @click="saveAccountInfo" :disabled="isSaving" prepend-icon="mdi-content-save">
|
||||||
<span v-if="isSaving">{{ t('common.saving') }}...</span>
|
<span v-if="isSaving">{{ t('common.saving') }}...</span>
|
||||||
<span v-else>{{ t('common.save') }}</span>
|
<span v-else>{{ t('common.save') }}</span>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
@@ -462,6 +478,7 @@ watch(
|
|||||||
autocomplete=""
|
autocomplete=""
|
||||||
class="mb-8"
|
class="mb-8"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
prepend-inner-icon="mdi-shield-key"
|
||||||
/>
|
/>
|
||||||
<div class="d-flex justify-end flex-wrap gap-4">
|
<div class="d-flex justify-end flex-wrap gap-4">
|
||||||
<VBtn variant="outlined" color="secondary" @click="otpDialog = false"> {{ t('common.cancel') }} </VBtn>
|
<VBtn variant="outlined" color="secondary" @click="otpDialog = false"> {{ t('common.cancel') }} </VBtn>
|
||||||
|
|||||||
Reference in New Issue
Block a user