mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-10 17:42:50 +08:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a738d4a3b9 | ||
|
|
e9866a04df | ||
|
|
4f5193d602 | ||
|
|
37b92c55ba | ||
|
|
9299f1bcb6 | ||
|
|
7fe12192df | ||
|
|
1169644ab3 | ||
|
|
6f7770ed43 | ||
|
|
8059fd6f90 | ||
|
|
556dbd8d78 | ||
|
|
6695fd8c14 | ||
|
|
3ab0229275 | ||
|
|
99467127a0 | ||
|
|
90d73b7bd5 | ||
|
|
2e326e1798 | ||
|
|
251eac93c7 | ||
|
|
c74d70808c | ||
|
|
e63b2d7152 | ||
|
|
16b29b56a5 | ||
|
|
6d79c4fe2f | ||
|
|
4b1fb60ee3 | ||
|
|
1d2be54f9e | ||
|
|
83547e32db | ||
|
|
70ddb929f2 | ||
|
|
8b22961394 | ||
|
|
c15d42c179 | ||
|
|
098e473cab | ||
|
|
f6f3d9368a | ||
|
|
9558a420e9 | ||
|
|
4d3b69ca34 | ||
|
|
fdcc4a44c8 | ||
|
|
5de0494538 | ||
|
|
2045f833e4 | ||
|
|
cc4f89aac1 |
@@ -1 +1 @@
|
||||
VITE_API_BASE_URL=/api/v1/
|
||||
VITE_API_BASE_URL=api/v1/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "moviepilot",
|
||||
"version": "1.5.7-3",
|
||||
"version": "1.6.4-1",
|
||||
"private": true,
|
||||
"bin": "dist/service.js",
|
||||
"scripts": {
|
||||
@@ -24,6 +24,7 @@
|
||||
"@floating-ui/dom": "1.2.8",
|
||||
"@vueuse/core": "^10.1.2",
|
||||
"@vueuse/math": "^10.1.2",
|
||||
"ace-builds": "^1.32.6",
|
||||
"apexcharts-clevision": "^3.28.5",
|
||||
"axios": "1.4.0",
|
||||
"axios-mock-adapter": "^1.21.4",
|
||||
@@ -48,6 +49,7 @@
|
||||
"vue-prism-component": "^2.0.0",
|
||||
"vue-router": "^4.2.0",
|
||||
"vue-toast-notification": "^3",
|
||||
"vue3-ace-editor": "^2.2.4",
|
||||
"vue3-apexcharts": "^1.4.1",
|
||||
"vue3-perfect-scrollbar": "^1.6.0",
|
||||
"vuetify": "3.3.5",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { useTheme } from 'vuetify'
|
||||
import type { ThemeSwitcherTheme } from '@layouts/types'
|
||||
|
||||
@@ -20,26 +20,30 @@ const {
|
||||
{ initialValue: savedTheme.value },
|
||||
)
|
||||
|
||||
function changeTheme() {
|
||||
const nextTheme = getNextThemeName()
|
||||
|
||||
globalTheme.name.value = nextTheme
|
||||
savedTheme.value = nextTheme
|
||||
localStorage.setItem('theme', nextTheme)
|
||||
function updateTheme() {
|
||||
const autoTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
const theme = currentThemeName.value === 'auto' ? autoTheme : currentThemeName.value
|
||||
globalTheme.name.value = theme
|
||||
savedTheme.value = theme
|
||||
// 修改载入时背景色
|
||||
localStorage.setItem('materio-initial-loader-bg', globalTheme.current.value.colors.background)
|
||||
|
||||
themeTransition()
|
||||
}
|
||||
|
||||
// Update icon if theme is changed from other sources
|
||||
// 监听系统主题变化
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme)
|
||||
|
||||
watch(
|
||||
() => globalTheme.name.value,
|
||||
(val) => {
|
||||
currentThemeName.value = val
|
||||
},
|
||||
() => currentThemeName.value,
|
||||
() => updateTheme(),
|
||||
)
|
||||
|
||||
function changeTheme() {
|
||||
const nextTheme = getNextThemeName()
|
||||
currentThemeName.value = nextTheme
|
||||
localStorage.setItem('theme', nextTheme)
|
||||
}
|
||||
|
||||
// Apply saved theme on page load
|
||||
// onMounted(() => {
|
||||
// globalTheme.name.value = savedTheme.value
|
||||
|
||||
10
src/App.vue
10
src/App.vue
@@ -3,9 +3,15 @@ import { useToast } from 'vue-toast-notification'
|
||||
import { useTheme } from 'vuetify'
|
||||
import store from './store'
|
||||
|
||||
function setTheme() {
|
||||
const { global: globalTheme } = useTheme()
|
||||
let theme = localStorage.getItem('theme') || 'light'
|
||||
if (theme === 'auto')
|
||||
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
globalTheme.name.value = theme
|
||||
}
|
||||
// 第一时间应用主题
|
||||
const { global: globalTheme } = useTheme()
|
||||
globalTheme.name.value = localStorage.getItem('theme') || 'light'
|
||||
setTheme()
|
||||
|
||||
// 提示框
|
||||
const $toast = useToast()
|
||||
|
||||
54
src/ace-config.ts
Normal file
54
src/ace-config.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import ace from 'ace-builds'
|
||||
|
||||
import modeJsonUrl from 'ace-builds/src-noconflict/mode-json?url'
|
||||
|
||||
import modeJavascriptUrl from 'ace-builds/src-noconflict/mode-javascript?url'
|
||||
|
||||
import modeHtmlUrl from 'ace-builds/src-noconflict/mode-html?url'
|
||||
|
||||
import modeYamlUrl from 'ace-builds/src-noconflict/mode-yaml?url'
|
||||
|
||||
import themeGithubUrl from 'ace-builds/src-noconflict/theme-github?url'
|
||||
|
||||
import themeChromeUrl from 'ace-builds/src-noconflict/theme-chrome?url'
|
||||
|
||||
import themeMonokaiUrl from 'ace-builds/src-noconflict/theme-monokai?url'
|
||||
|
||||
import workerBaseUrl from 'ace-builds/src-noconflict/worker-base?url'
|
||||
|
||||
import workerJsonUrl from 'ace-builds/src-noconflict/worker-json?url'
|
||||
|
||||
import workerJavascriptUrl from 'ace-builds/src-noconflict/worker-javascript?url'
|
||||
|
||||
import workerHtmlUrl from 'ace-builds/src-noconflict/worker-html?url'
|
||||
|
||||
import workerYamlUrl from 'ace-builds/src-noconflict/worker-yaml?url'
|
||||
|
||||
import snippetsHtmlUrl from 'ace-builds/src-noconflict/snippets/html?url'
|
||||
|
||||
import snippetsJsUrl from 'ace-builds/src-noconflict/snippets/javascript?url'
|
||||
|
||||
import snippetsYamlUrl from 'ace-builds/src-noconflict/snippets/yaml?url'
|
||||
|
||||
import snippetsJsonUrl from 'ace-builds/src-noconflict/snippets/json?url'
|
||||
|
||||
import 'ace-builds/src-noconflict/ext-language_tools'
|
||||
|
||||
ace.config.setModuleUrl('ace/mode/json', modeJsonUrl)
|
||||
ace.config.setModuleUrl('ace/mode/javascript', modeJavascriptUrl)
|
||||
ace.config.setModuleUrl('ace/mode/html', modeHtmlUrl)
|
||||
ace.config.setModuleUrl('ace/mode/yaml', modeYamlUrl)
|
||||
ace.config.setModuleUrl('ace/theme/github', themeGithubUrl)
|
||||
ace.config.setModuleUrl('ace/theme/chrome', themeChromeUrl)
|
||||
ace.config.setModuleUrl('ace/theme/monokai', themeMonokaiUrl)
|
||||
ace.config.setModuleUrl('ace/mode/base', workerBaseUrl)
|
||||
ace.config.setModuleUrl('ace/mode/json_worker', workerJsonUrl)
|
||||
ace.config.setModuleUrl('ace/mode/javascript_worker', workerJavascriptUrl)
|
||||
ace.config.setModuleUrl('ace/mode/html_worker', workerHtmlUrl)
|
||||
ace.config.setModuleUrl('ace/mode/yaml_worker', workerYamlUrl)
|
||||
ace.config.setModuleUrl('ace/snippets/html', snippetsHtmlUrl)
|
||||
ace.config.setModuleUrl('ace/snippets/javascript', snippetsJsUrl)
|
||||
ace.config.setModuleUrl('ace/snippets/javascript', snippetsYamlUrl)
|
||||
ace.config.setModuleUrl('ace/snippets/json', snippetsJsonUrl)
|
||||
|
||||
ace.require('ace/ext/language_tools')
|
||||
@@ -25,7 +25,7 @@ function goPlay() {
|
||||
// 计算图片地址
|
||||
const getImgUrl = computed(() => {
|
||||
const image = props.media?.image || ''
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(image)}`
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(image)}/0`
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ function getSpeedText() {
|
||||
// 下载状态
|
||||
const isDownloading = ref(props.info?.state === 'downloading')
|
||||
|
||||
// 监听props.info?.state的变化
|
||||
watch(() => props.info?.state, (newValue) => {
|
||||
isDownloading.value = newValue === 'downloading';
|
||||
});
|
||||
|
||||
// 图片是否加载完成
|
||||
const imageLoaded = ref(false)
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ function getImgUrl(url: string) {
|
||||
if (!url)
|
||||
return getDefaultImage()
|
||||
else
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/proxy?url=${encodeURIComponent(url)}`
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(url)}/0`
|
||||
}
|
||||
|
||||
// 根据多张图片生成媒体库封面
|
||||
@@ -68,7 +68,7 @@ async function drawImages(imageList: string[]) {
|
||||
|
||||
// 为所有图片添加system/img前缀
|
||||
for (let i = 0; i < IMAGES.length; i++)
|
||||
IMAGES[i] = `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(IMAGES[i])}`
|
||||
IMAGES[i] = `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(IMAGES[i])}/0`
|
||||
|
||||
// canvas
|
||||
const canvas = canvasRef.value
|
||||
|
||||
@@ -85,9 +85,9 @@ const iconPath: Ref<string> = computed(() => {
|
||||
return noImage
|
||||
// 如果是网络图片则使用代理后返回
|
||||
if (props.plugin?.plugin_icon?.startsWith('http'))
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(props.plugin?.plugin_icon)}`
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(props.plugin?.plugin_icon)}/1`
|
||||
|
||||
return `/plugin_icon/${props.plugin?.plugin_icon}`
|
||||
return `./plugin_icon/${props.plugin?.plugin_icon}`
|
||||
})
|
||||
|
||||
// 访问插件页面
|
||||
|
||||
@@ -181,9 +181,9 @@ const iconPath: Ref<string> = computed(() => {
|
||||
return noImage
|
||||
// 如果是网络图片则使用代理后返回
|
||||
if (props.plugin?.plugin_icon?.startsWith('http'))
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(props.plugin?.plugin_icon)}`
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(props.plugin?.plugin_icon)}/1`
|
||||
|
||||
return `/plugin_icon/${props.plugin?.plugin_icon}`
|
||||
return `./plugin_icon/${props.plugin?.plugin_icon}`
|
||||
})
|
||||
|
||||
// 重置插件
|
||||
|
||||
@@ -31,7 +31,7 @@ const getImgUrl = computed(() => {
|
||||
if (imageLoadError.value)
|
||||
return noImage
|
||||
const image = props.media?.image || ''
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(image)}`
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(image)}/0`
|
||||
})
|
||||
|
||||
// 跳转播放
|
||||
|
||||
@@ -80,6 +80,7 @@ const resourceItemsPerPage = ref(25)
|
||||
const userPwForm = ref({
|
||||
username: '',
|
||||
password: '',
|
||||
code: '',
|
||||
})
|
||||
|
||||
// 打开种子详情页面
|
||||
@@ -152,6 +153,7 @@ async function updateSiteCookie() {
|
||||
params: {
|
||||
username: userPwForm.value.username,
|
||||
password: userPwForm.value.password,
|
||||
code: userPwForm.value.code,
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -335,7 +337,7 @@ onMounted(() => {
|
||||
<VRow>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
md="4"
|
||||
>
|
||||
<VTextField
|
||||
v-model="userPwForm.username"
|
||||
@@ -345,7 +347,7 @@ onMounted(() => {
|
||||
</VCol>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
md="4"
|
||||
>
|
||||
<VTextField
|
||||
v-model="userPwForm.password"
|
||||
@@ -359,6 +361,15 @@ onMounted(() => {
|
||||
@keydown.enter="updateSiteCookie"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="4"
|
||||
>
|
||||
<VTextField
|
||||
v-model="userPwForm.code"
|
||||
label="两步验证"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
|
||||
@@ -267,6 +267,28 @@ async function recognize(path: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// 调用API刮削
|
||||
async function scrape(path: string) {
|
||||
try {
|
||||
// 显示进度条
|
||||
progressDialog.value = true
|
||||
progressText.value = `正在刮削 ${path} ...`
|
||||
const result: { [key: string]: any } = await api.get('media/scrape', {
|
||||
params: {
|
||||
path,
|
||||
},
|
||||
})
|
||||
// 关闭进度条
|
||||
progressDialog.value = false
|
||||
if (!result.success)
|
||||
$toast.error(result.message)
|
||||
else
|
||||
$toast.success(`${path}削刮完成!`)
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
// 弹出菜单
|
||||
const dropdownItems = ref([
|
||||
{
|
||||
@@ -279,8 +301,17 @@ const dropdownItems = ref([
|
||||
},
|
||||
},
|
||||
}, {
|
||||
title: '重命名',
|
||||
title: '刮削',
|
||||
value: 2,
|
||||
props: {
|
||||
prependIcon: 'mdi-auto-fix',
|
||||
click: (_item: FileItem) => {
|
||||
scrape(_item.path || '')
|
||||
},
|
||||
},
|
||||
}, {
|
||||
title: '重命名',
|
||||
value: 3,
|
||||
props: {
|
||||
prependIcon: 'mdi-rename',
|
||||
click: showRenmae,
|
||||
@@ -288,7 +319,7 @@ const dropdownItems = ref([
|
||||
},
|
||||
{
|
||||
title: '整理',
|
||||
value: 3,
|
||||
value: 4,
|
||||
props: {
|
||||
prependIcon: 'mdi-folder-arrow-right',
|
||||
click: showTransfer,
|
||||
@@ -296,7 +327,7 @@ const dropdownItems = ref([
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
value: 4,
|
||||
value: 5,
|
||||
props: {
|
||||
prependIcon: 'mdi-delete-outline',
|
||||
color: 'error',
|
||||
@@ -345,111 +376,133 @@ onMounted(() => {
|
||||
<VCardText v-else-if="dirs.length || files.length" class="p-0">
|
||||
<VList v-if="dirs.length" subheader>
|
||||
<VListSubheader>目录</VListSubheader>
|
||||
<VListItem
|
||||
<VHover
|
||||
v-for="(item, index) in dirs"
|
||||
:key="index"
|
||||
class="px-3 pe-1"
|
||||
@click="changePath(item.path)"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-folder-outline" />
|
||||
</template>
|
||||
<VListItemTitle v-text="item.name" />
|
||||
<template #append>
|
||||
<IconBtn class="d-sm-none">
|
||||
<VIcon
|
||||
icon="mdi-dots-vertical"
|
||||
/>
|
||||
<VMenu
|
||||
activator="parent"
|
||||
close-on-content-click
|
||||
>
|
||||
<VList>
|
||||
<VListItem
|
||||
v-for="(menu, i) in dropdownItems"
|
||||
:key="i"
|
||||
variant="plain"
|
||||
:base-color="menu.props.color"
|
||||
@click="menu.props.click(item)"
|
||||
<template #default="hover">
|
||||
<VListItem
|
||||
v-bind="hover.props"
|
||||
class="px-3 pe-1"
|
||||
@click="changePath(item.path)"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-folder-outline" />
|
||||
</template>
|
||||
<VListItemTitle v-text="item.name" />
|
||||
<template #append>
|
||||
<IconBtn class="d-sm-none">
|
||||
<VIcon
|
||||
icon="mdi-dots-vertical"
|
||||
/>
|
||||
<VMenu
|
||||
activator="parent"
|
||||
close-on-content-click
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon :icon="menu.props.prependIcon" />
|
||||
</template>
|
||||
<VListItemTitle v-text="menu.title" />
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="recognize(item.path)">
|
||||
<VIcon icon="mdi-text-recognition" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="showRenmae(item)">
|
||||
<VIcon icon="mdi-rename" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="showTransfer(item)">
|
||||
<VIcon icon="mdi-folder-arrow-right" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="deleteItem(item)">
|
||||
<VIcon icon="mdi-delete-outline" />
|
||||
</IconBtn>
|
||||
<VList>
|
||||
<VListItem
|
||||
v-for="(menu, i) in dropdownItems"
|
||||
:key="i"
|
||||
variant="plain"
|
||||
:base-color="menu.props.color"
|
||||
@click="menu.props.click(item)"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon :icon="menu.props.prependIcon" />
|
||||
</template>
|
||||
<VListItemTitle v-text="menu.title" />
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
</IconBtn>
|
||||
<span v-show="hover.isHovering" class="flex">
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="recognize(item.path)">
|
||||
<VIcon icon="mdi-text-recognition" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="scrape(item.path)">
|
||||
<VIcon icon="mdi-auto-fix" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="showRenmae(item)">
|
||||
<VIcon icon="mdi-rename" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="showTransfer(item)">
|
||||
<VIcon icon="mdi-folder-arrow-right" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="deleteItem(item)">
|
||||
<VIcon icon="mdi-delete-outline" />
|
||||
</IconBtn>
|
||||
</span>
|
||||
</template>
|
||||
</VListItem>
|
||||
</template>
|
||||
</VListItem>
|
||||
</VHover>
|
||||
</VList>
|
||||
<VDivider v-if="dirs.length && files.length" />
|
||||
<VList v-if="files.length" subheader>
|
||||
<VListSubheader>文件</VListSubheader>
|
||||
<VListItem
|
||||
<VHover
|
||||
v-for="(item, index) in files"
|
||||
:key="index"
|
||||
class="pl-3 pe-1"
|
||||
@click="changePath(item.path)"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon v-if="inProps.icons" :icon="inProps.icons[item.extension.toLowerCase()] || inProps.icons?.other" />
|
||||
</template>
|
||||
<template #default="hover">
|
||||
<VListItem
|
||||
v-bind="hover.props"
|
||||
class="pl-3 pe-1"
|
||||
@click="changePath(item.path)"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon v-if="inProps.icons" :icon="inProps.icons[item.extension.toLowerCase()] || inProps.icons?.other" />
|
||||
</template>
|
||||
|
||||
<VListItemTitle v-text="item.name" />
|
||||
<VListItemSubtitle> {{ formatBytes(item.size) }}</VListItemSubtitle>
|
||||
<VListItemTitle v-text="item.name" />
|
||||
<VListItemSubtitle> {{ formatBytes(item.size) }}</VListItemSubtitle>
|
||||
|
||||
<template #append>
|
||||
<IconBtn class="d-sm-none">
|
||||
<VIcon
|
||||
icon="mdi-dots-vertical"
|
||||
/>
|
||||
<VMenu
|
||||
activator="parent"
|
||||
close-on-content-click
|
||||
>
|
||||
<VList>
|
||||
<VListItem
|
||||
v-for="(menu, i) in dropdownItems"
|
||||
:key="i"
|
||||
variant="plain"
|
||||
:base-color="menu.props.color"
|
||||
@click="menu.props.click(item)"
|
||||
<template #append>
|
||||
<IconBtn class="d-sm-none">
|
||||
<VIcon
|
||||
icon="mdi-dots-vertical"
|
||||
/>
|
||||
<VMenu
|
||||
activator="parent"
|
||||
close-on-content-click
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon :icon="menu.props.prependIcon" />
|
||||
</template>
|
||||
<VListItemTitle v-text="menu.title" />
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="recognize(item.path)">
|
||||
<VIcon icon="mdi-text-recognition" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="showRenmae(item)">
|
||||
<VIcon icon="mdi-rename" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="showTransfer(item)">
|
||||
<VIcon icon="mdi-folder-arrow-right" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="deleteItem(item)">
|
||||
<VIcon icon="mdi-delete-outline" />
|
||||
</IconBtn>
|
||||
<VList>
|
||||
<VListItem
|
||||
v-for="(menu, i) in dropdownItems"
|
||||
:key="i"
|
||||
variant="plain"
|
||||
:base-color="menu.props.color"
|
||||
@click="menu.props.click(item)"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon :icon="menu.props.prependIcon" />
|
||||
</template>
|
||||
<VListItemTitle v-text="menu.title" />
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
</IconBtn>
|
||||
<span v-show="hover.isHovering" class="flex">
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="recognize(item.path)">
|
||||
<VIcon icon="mdi-text-recognition" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="scrape(item.path)">
|
||||
<VIcon icon="mdi-auto-fix" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="showRenmae(item)">
|
||||
<VIcon icon="mdi-rename" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="showTransfer(item)">
|
||||
<VIcon icon="mdi-folder-arrow-right" />
|
||||
</IconBtn>
|
||||
<IconBtn class="d-none d-sm-block" @click.stop="deleteItem(item)">
|
||||
<VIcon icon="mdi-delete-outline" />
|
||||
</IconBtn>
|
||||
</span>
|
||||
</template>
|
||||
</VListItem>
|
||||
</template>
|
||||
</VListItem>
|
||||
</VHover>
|
||||
</VList>
|
||||
</VCardText>
|
||||
<VCardText
|
||||
|
||||
@@ -326,7 +326,6 @@ watchEffect(() => {
|
||||
<VRow>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="4"
|
||||
>
|
||||
<VTextField
|
||||
v-model="subscribeForm.save_path"
|
||||
|
||||
@@ -32,23 +32,58 @@ const formData = ref<any>(elementProps.form || {})
|
||||
<template>
|
||||
<Component
|
||||
:is="formItem.component"
|
||||
v-if="!formItem.html"
|
||||
v-if="!formItem.html && !!formItem.props?.modelvalue"
|
||||
v-bind="formItem.props"
|
||||
v-model="formData[formItem.props?.model || '']"
|
||||
v-model:value="formData[formItem.props?.modelvalue]"
|
||||
>
|
||||
{{ formItem.text }}
|
||||
<FormRender
|
||||
<template
|
||||
v-for="(innerItem, innerIndex) in (formItem.content || [])"
|
||||
:key="innerIndex"
|
||||
v-model="formData[innerItem.props?.model || '']"
|
||||
:config="innerItem"
|
||||
:form="formData"
|
||||
/>
|
||||
>
|
||||
<FormRender
|
||||
v-if="!!innerItem.props?.modelvalue"
|
||||
v-model:value="formData[innerItem.props?.modelvalue]"
|
||||
:config="innerItem"
|
||||
:form="formData"
|
||||
/>
|
||||
<FormRender
|
||||
v-else
|
||||
v-model="formData[innerItem.props?.model]"
|
||||
:config="innerItem"
|
||||
:form="formData"
|
||||
/>
|
||||
</template>
|
||||
</Component>
|
||||
<Component
|
||||
:is="formItem.component"
|
||||
v-if="formItem.html"
|
||||
v-else-if="formItem.html"
|
||||
v-bind="formItem.props"
|
||||
v-html="formItem.html"
|
||||
/>
|
||||
<Component
|
||||
:is="formItem.component"
|
||||
v-else
|
||||
v-bind="formItem.props"
|
||||
v-model="formData[formItem.props?.model]"
|
||||
>
|
||||
{{ formItem.text }}
|
||||
<template
|
||||
v-for="(innerItem, innerIndex) in (formItem.content || [])"
|
||||
:key="innerIndex"
|
||||
>
|
||||
<FormRender
|
||||
v-if="!!innerItem.props?.modelvalue"
|
||||
v-model:value="formData[innerItem.props?.modelvalue]"
|
||||
:config="innerItem"
|
||||
:form="formData"
|
||||
/>
|
||||
<FormRender
|
||||
v-else
|
||||
v-model="formData[innerItem.props?.model]"
|
||||
:config="innerItem"
|
||||
:form="formData"
|
||||
/>
|
||||
</template>
|
||||
</Component>
|
||||
</template>
|
||||
|
||||
@@ -14,6 +14,10 @@ const themes: ThemeSwitcherTheme[] = [
|
||||
name: 'purple',
|
||||
icon: 'mdi-brightness-4',
|
||||
},
|
||||
{
|
||||
name: 'auto',
|
||||
icon: 'mdi-brightness-auto',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import NameTestView from '@/views/system/NameTestView.vue'
|
||||
import NetTestView from '@/views/system/NetTestView.vue'
|
||||
import LoggingView from '@/views/system/LoggingView.vue'
|
||||
import RuleTestView from '@/views/system/RuleTestView.vue'
|
||||
import store from '@/store'
|
||||
|
||||
// App捷径
|
||||
const appsMenu = ref(false)
|
||||
@@ -18,6 +19,12 @@ const loggingDialog = ref(false)
|
||||
|
||||
// 过滤规则弹窗
|
||||
const ruleTestDialog = ref(false)
|
||||
|
||||
// 拼接全部日志url
|
||||
function allLoggingUrl() {
|
||||
const token = store.state.auth.token
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/logging?token=${token}&length=-1`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -171,8 +178,19 @@ const ruleTestDialog = ref(false)
|
||||
class="w-full lg:w-4/5"
|
||||
scrollable
|
||||
>
|
||||
<VCard title="实时日志">
|
||||
<VCard>
|
||||
<DialogCloseBtn @click="loggingDialog = false" />
|
||||
<VCardItem>
|
||||
<VCardTitle class="inline-flex">
|
||||
实时日志
|
||||
<a class="mx-2 inline-flex items-center justify-center" :href="allLoggingUrl()" target="_blank">
|
||||
<div class="inline-flex cursor-pointer items-center rounded-full bg-gray-600 px-2 text-sm text-gray-200 ring-1 ring-gray-500 transition hover:bg-gray-700">
|
||||
<VIcon icon="mdi-open-in-new" />
|
||||
<span class="ms-1">在新窗口中打开</span>
|
||||
</div>
|
||||
</a>
|
||||
</VCardTitle>
|
||||
</VCardItem>
|
||||
<VCardText>
|
||||
<LoggingView />
|
||||
</VCardText>
|
||||
|
||||
11
src/main.ts
11
src/main.ts
@@ -1,7 +1,10 @@
|
||||
import { VAceEditor } from 'vue3-ace-editor'
|
||||
import { createApp } from 'vue'
|
||||
import '@/@iconify/icons-bundle'
|
||||
import ToastPlugin from 'vue-toast-notification'
|
||||
import VuetifyUseDialog from 'vuetify-use-dialog'
|
||||
import './ace-config'
|
||||
import VueApexCharts from 'vue3-apexcharts'
|
||||
import { removeEl } from './@core/utils/dom'
|
||||
import App from '@/App.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
@@ -15,10 +18,14 @@ import 'vue-toast-notification/dist/theme-bootstrap.css'
|
||||
|
||||
loadFonts()
|
||||
|
||||
// Create vue app
|
||||
// 创建Vue实例
|
||||
const app = createApp(App)
|
||||
|
||||
// Use plugins Mount vue app
|
||||
// 注册全局组件
|
||||
app.component('VAceEditor', VAceEditor)
|
||||
.component('VApexChart', VueApexCharts)
|
||||
|
||||
// 注册插件
|
||||
app
|
||||
.use(vuetify)
|
||||
.use(router)
|
||||
|
||||
@@ -8,6 +8,7 @@ import AccountSettingAbout from '@/views/setting/AccountSettingAbout.vue'
|
||||
import AccountSettingSearch from '@/views/setting/AccountSettingSearch.vue'
|
||||
import AccountSettingSubscribe from '@/views/setting/AccountSettingSubscribe.vue'
|
||||
import AccountSettingService from '@/views/setting/AccountSettingService.vue'
|
||||
import AccountSettingSystem from '@/views/setting/AccountSettingSystem.vue'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
@@ -20,6 +21,11 @@ const tabs = [
|
||||
icon: 'mdi-account',
|
||||
tab: 'account',
|
||||
},
|
||||
{
|
||||
title: '系统',
|
||||
icon: 'mdi-cog',
|
||||
tab: 'system',
|
||||
},
|
||||
{
|
||||
title: '站点',
|
||||
icon: 'mdi-web',
|
||||
@@ -83,6 +89,13 @@ const tabs = [
|
||||
</transition>
|
||||
</VWindowItem>
|
||||
|
||||
<!-- 系统 -->
|
||||
<VWindowItem value="system">
|
||||
<transition name="fade-slide" appear>
|
||||
<AccountSettingSystem />
|
||||
</transition>
|
||||
</VWindowItem>
|
||||
|
||||
<!-- 站点 -->
|
||||
<VWindowItem value="site">
|
||||
<transition name="fade-slide" appear>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { configureNProgress, doneNProgress, startNProgress } from '@/api/nprogress'
|
||||
import store from '@/store'
|
||||
|
||||
@@ -7,7 +7,7 @@ configureNProgress()
|
||||
|
||||
// Router
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
// 如果页面有缓存那么恢复其位置, 否则始终滚动到顶部
|
||||
if (to.meta.keepAlive && savedPosition)
|
||||
|
||||
@@ -544,7 +544,9 @@ onBeforeMount(() => {
|
||||
<ul v-if="mediaDetail.tmdb_id" class="media-crew">
|
||||
<li v-for="director in mediaDetail.directors" :key="director.id">
|
||||
<span>{{ director.job }}</span>
|
||||
<a class="crew-name" :href="`person?personid=${director.id}`" target="_blank">{{ director.name }}</a>
|
||||
<RouterLink :to="`/person?personid=${director.id}`" class="crew-name" target="_blank">
|
||||
{{ director.name }}
|
||||
</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="!mediaDetail.tmdb_id && mediaDetail.douban_id" class="media-crew">
|
||||
|
||||
@@ -5,6 +5,29 @@ import type { NotificationSwitch } from '@/api/types'
|
||||
|
||||
const messagemTypes = ref<NotificationSwitch[]>([])
|
||||
|
||||
// 选中的消息渠道
|
||||
const selectedChannels = ref([])
|
||||
|
||||
// 消息渠道
|
||||
const NotificationChannels = [
|
||||
{
|
||||
title: '微信',
|
||||
value: 'wechat',
|
||||
},
|
||||
{
|
||||
title: 'Telegram',
|
||||
value: 'telegram',
|
||||
},
|
||||
{
|
||||
title: 'Slack',
|
||||
value: 'slack',
|
||||
},
|
||||
{
|
||||
title: 'SynologyChat',
|
||||
value: 'synologychat',
|
||||
},
|
||||
]
|
||||
|
||||
// 提示框
|
||||
const $toast = useToast()
|
||||
|
||||
@@ -40,79 +63,147 @@ async function saveNotificationSwitchs() {
|
||||
}
|
||||
}
|
||||
|
||||
// 调用API查询消息渠道设置
|
||||
async function loadNotificationChannels() {
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.get('system/setting/MESSAGER')
|
||||
if (result.success)
|
||||
selectedChannels.value = result.data?.value?.split(',')
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用API保存消息渠道设置
|
||||
async function saveNotificationChannels() {
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.post(
|
||||
'system/setting/MESSAGER',
|
||||
selectedChannels.value.join(','),
|
||||
)
|
||||
|
||||
if (result.success)
|
||||
$toast.success('保存通知渠道设置成功')
|
||||
else
|
||||
$toast.error('保存通知渠道设置失败!')
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadNotificationSwitchs()
|
||||
loadNotificationChannels()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VCard title="消息通知">
|
||||
<VCardText> 对应消息类型只会发送给选中的消息渠道。 </VCardText>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VCard title="通知渠道">
|
||||
<VCardSubtitle>只有选中的渠道才会发送消息。</VCardSubtitle>
|
||||
<VCardText>
|
||||
<VForm>
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="selectedChannels"
|
||||
multiple
|
||||
chips
|
||||
:items="NotificationChannels"
|
||||
label="当前使用通知渠道"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
<VCardText>
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||
<VBtn
|
||||
mtype="submit"
|
||||
@click="saveNotificationChannels"
|
||||
>
|
||||
保存
|
||||
</VBtn>
|
||||
</div>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VCard title="消息类型">
|
||||
<VCardSubtitle> 对应消息类型只会发送给选中的消息渠道。 </VCardSubtitle>
|
||||
<VTable class="text-no-wrap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
消息类型
|
||||
</th>
|
||||
<th scope="col">
|
||||
微信
|
||||
</th>
|
||||
<th scope="col">
|
||||
Telegram
|
||||
</th>
|
||||
<th scope="col">
|
||||
Slack
|
||||
</th>
|
||||
<th scope="col">
|
||||
SynologyChat
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="message in messagemTypes"
|
||||
:key="message.mtype"
|
||||
>
|
||||
<td>
|
||||
{{ message.mtype }}
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="message.wechat" />
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="message.telegram" />
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="message.slack" />
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="message.synologychat" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="messagemTypes.length === 0">
|
||||
<td
|
||||
colspan="4"
|
||||
class="text-center"
|
||||
>
|
||||
没有设置任何通知渠道
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</VTable>
|
||||
<VDivider />
|
||||
|
||||
<VTable class="text-no-wrap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
消息类型
|
||||
</th>
|
||||
<th scope="col">
|
||||
微信
|
||||
</th>
|
||||
<th scope="col">
|
||||
Telegram
|
||||
</th>
|
||||
<th scope="col">
|
||||
Slack
|
||||
</th>
|
||||
<th scope="col">
|
||||
SynologyChat
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="message in messagemTypes"
|
||||
:key="message.mtype"
|
||||
>
|
||||
<td>
|
||||
{{ message.mtype }}
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="message.wechat" />
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="message.telegram" />
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="message.slack" />
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="message.synologychat" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="messagemTypes.length === 0">
|
||||
<td
|
||||
colspan="4"
|
||||
class="text-center"
|
||||
>
|
||||
没有设置任何通知渠道
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</VTable>
|
||||
<VDivider />
|
||||
|
||||
<VCardText>
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||
<VBtn
|
||||
mtype="submit"
|
||||
@click="saveNotificationSwitchs"
|
||||
>
|
||||
保存
|
||||
</VBtn>
|
||||
</div>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
<VCardText>
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||
<VBtn
|
||||
mtype="submit"
|
||||
@click="saveNotificationSwitchs"
|
||||
>
|
||||
保存
|
||||
</VBtn>
|
||||
</div>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</template>
|
||||
|
||||
@@ -78,7 +78,7 @@ onUnmounted(() => {
|
||||
|
||||
<template>
|
||||
<VCard title="定时作业">
|
||||
<VCardText> 手动执行不会影响作业正常的时间表。 </VCardText>
|
||||
<VCardSubtitle> 手动执行不会影响作业正常的时间表。 </VCardSubtitle>
|
||||
|
||||
<VTable class="text-no-wrap">
|
||||
<thead>
|
||||
|
||||
@@ -94,7 +94,7 @@ onMounted(() => {
|
||||
<VSelect
|
||||
v-model="selectedTorrentPriority"
|
||||
:items="TorrentPriorityItems"
|
||||
label="优先规则"
|
||||
label="当前使用下载优先规则"
|
||||
outlined
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
194
src/views/setting/AccountSettingSystem.vue
Normal file
194
src/views/setting/AccountSettingSystem.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<script lang="ts" setup>
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import api from '@/api'
|
||||
|
||||
// 选中的下载器
|
||||
const selectedDownloader = ref('')
|
||||
|
||||
// 选中的媒体服务器
|
||||
const selectedMediaServers = ref([])
|
||||
|
||||
// 是否下载器监控
|
||||
const downloaderMonitor = ref<boolean>(false)
|
||||
|
||||
// 下载器
|
||||
const Downloaders = [
|
||||
{
|
||||
title: 'Qbittorrent',
|
||||
value: 'qbittorrent',
|
||||
},
|
||||
{
|
||||
title: 'Transmission',
|
||||
value: 'transmission',
|
||||
},
|
||||
]
|
||||
|
||||
// 媒体服务器
|
||||
const MediaServers = [
|
||||
{
|
||||
title: 'Emby',
|
||||
value: 'emby',
|
||||
},
|
||||
{
|
||||
title: 'Jellyfin',
|
||||
value: 'jellyfin',
|
||||
},
|
||||
{
|
||||
title: 'Plex',
|
||||
value: 'plex',
|
||||
},
|
||||
]
|
||||
|
||||
// 提示框
|
||||
const $toast = useToast()
|
||||
|
||||
// 调用API查询下载器设置
|
||||
async function loadDownladerSetting() {
|
||||
try {
|
||||
const result1: { [key: string]: any } = await api.get('system/setting/DOWNLOADER')
|
||||
if (result1.success)
|
||||
selectedDownloader.value = result1.data?.value
|
||||
const result2: { [key: string]: any } = await api.get('system/setting/DOWNLOADER_MONITOR')
|
||||
if (result2.success)
|
||||
downloaderMonitor.value = result2.data?.value
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用API保存下载器设置
|
||||
async function saveDownloaderSetting() {
|
||||
try {
|
||||
const result1: { [key: string]: any } = await api.post(
|
||||
'system/setting/DOWNLOADER',
|
||||
selectedDownloader.value,
|
||||
)
|
||||
|
||||
const result2: { [key: string]: any } = await api.post(
|
||||
'system/setting/DOWNLOADER_MONITOR',
|
||||
downloaderMonitor.value,
|
||||
)
|
||||
|
||||
if (result1.success && result2.success)
|
||||
$toast.success('保存下载器设置成功')
|
||||
else
|
||||
$toast.error('保存下载器设置失败!')
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用API查询媒体服务器设置
|
||||
async function loadMediaServerSetting() {
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.get('system/setting/MEDIASERVER')
|
||||
if (result.success)
|
||||
selectedMediaServers.value = result.data?.value?.split(',')
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用API保存媒体服务器设置
|
||||
async function saveMediaServerSetting() {
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.post(
|
||||
'system/setting/MEDIASERVER',
|
||||
selectedMediaServers.value.join(','),
|
||||
)
|
||||
|
||||
if (result.success)
|
||||
$toast.success('保存媒体服务器设置成功')
|
||||
else
|
||||
$toast.error('保存媒体服务器设置失败!')
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadDownladerSetting()
|
||||
loadMediaServerSetting()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VCard title="下载器">
|
||||
<VCardSubtitle>只有选中的下载器才会被默认使用。</VCardSubtitle>
|
||||
<VCardText>
|
||||
<VForm>
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="selectedDownloader"
|
||||
:items="Downloaders"
|
||||
label="当前使用下载器"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VSwitch
|
||||
v-model="downloaderMonitor"
|
||||
label="监控下载器"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
<VCardText>
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||
<VBtn
|
||||
mtype="submit"
|
||||
@click="saveDownloaderSetting"
|
||||
>
|
||||
保存
|
||||
</VBtn>
|
||||
</div>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VCard title="媒体服务器">
|
||||
<VCardSubtitle>只有选中的媒体服务器才会被默认使用。</VCardSubtitle>
|
||||
<VCardText>
|
||||
<VForm>
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="selectedMediaServers"
|
||||
multiple
|
||||
chips
|
||||
:items="MediaServers"
|
||||
label="当前使用媒体服务器"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
<VCardText>
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||
<VBtn
|
||||
mtype="submit"
|
||||
@click="saveMediaServerSetting"
|
||||
>
|
||||
保存
|
||||
</VBtn>
|
||||
</div>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</template>
|
||||
@@ -9,6 +9,7 @@ import vuetify from 'vite-plugin-vuetify'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
plugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
@@ -27,7 +28,16 @@ export default defineConfig({
|
||||
imports: ['vue', 'vue-router', '@vueuse/core', '@vueuse/math', 'vuex'],
|
||||
vueTemplate: true,
|
||||
}),
|
||||
VitePWA({ registerType: 'autoUpdate', injectRegister: 'script', manifest: false }),
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
injectRegister: 'script',
|
||||
manifest: false,
|
||||
workbox: {
|
||||
navigateFallbackDenylist: [
|
||||
/.*\/api\/v\d+\/system\/logging.*/,
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
define: { 'process.env': {} },
|
||||
resolve: {
|
||||
|
||||
17
yarn.lock
17
yarn.lock
@@ -2422,6 +2422,11 @@ accepts@~1.3.8:
|
||||
mime-types "~2.1.34"
|
||||
negotiator "0.6.3"
|
||||
|
||||
ace-builds@^1.32.6:
|
||||
version "1.32.6"
|
||||
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.32.6.tgz#454ec8bc9235fbb960b8d8b86e698f941c104de2"
|
||||
integrity sha512-dO5BnyDOhCnznhOpILzXq4jqkbhRXxNkf3BuVTmyxGyRLrhddfdyk6xXgy+7A8LENrcYoFi/sIxMuH3qjNUN4w==
|
||||
|
||||
acorn-jsx@^5.2.0, acorn-jsx@^5.3.2:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
@@ -6651,6 +6656,11 @@ require-from-string@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
||||
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
|
||||
|
||||
resize-observer-polyfill@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
|
||||
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||
|
||||
resolve-from@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||
@@ -7912,6 +7922,13 @@ vue-tsc@^1.6.5:
|
||||
"@volar/vue-typescript" "1.6.5"
|
||||
semver "^7.3.8"
|
||||
|
||||
vue3-ace-editor@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vue3-ace-editor/-/vue3-ace-editor-2.2.4.tgz#1f2a787f91cf7979f27fab29e0e0604bb3ee1c17"
|
||||
integrity sha512-FZkEyfpbH068BwjhMyNROxfEI8135Sc+x8ouxkMdCNkuj/Tuw83VP/gStFQqZHqljyX9/VfMTCdTqtOnJZGN8g==
|
||||
dependencies:
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
vue3-apexcharts@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/vue3-apexcharts/-/vue3-apexcharts-1.4.1.tgz#ea561308430a1c5213b7f17c44ba3c845f6c490d"
|
||||
|
||||
Reference in New Issue
Block a user