fix fullscreen

This commit is contained in:
jxxghp
2024-04-25 09:48:12 +08:00
parent 6c71dce80c
commit 5c13362db2
11 changed files with 304 additions and 790 deletions
+72 -164
View File
@@ -14,7 +14,7 @@ import store from '@/store'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
@@ -74,12 +74,15 @@ const imageLoadError = ref(false)
const releaseDialog = ref(false) const releaseDialog = ref(false)
// 监听动作标识,如为true则打开详情 // 监听动作标识,如为true则打开详情
watch(() => props.action, (newAction, oldAction) => { watch(
if (newAction && !oldAction) { () => props.action,
openPluginDetail() (newAction, oldAction) => {
emit('actionDone') if (newAction && !oldAction) {
} openPluginDetail()
}) emit('actionDone')
}
},
)
// 图片加载完成 // 图片加载完成
async function imageLoaded() { async function imageLoaded() {
@@ -94,7 +97,7 @@ function showUpdateHistory() {
// 检查当前版本是否有更新日志 // 检查当前版本是否有更新日志
if (isNullOrEmptyObject(props.plugin?.history)) { if (isNullOrEmptyObject(props.plugin?.history)) {
updatePlugin() updatePlugin()
} else{ } else {
releaseDialog.value = true releaseDialog.value = true
} }
} }
@@ -114,11 +117,10 @@ async function uninstallPlugin() {
}, },
}) })
if (!isConfirmed) if (!isConfirmed) return
return
try { try {
// 显示等待提示框 // 显示等待提示框
progressDialog.value = true progressDialog.value = true
progressText.value = `正在卸载 ${props.plugin?.plugin_name} ...` progressText.value = `正在卸载 ${props.plugin?.plugin_name} ...`
const result: { [key: string]: any } = await api.delete(`plugin/${props.plugin?.id}`) const result: { [key: string]: any } = await api.delete(`plugin/${props.plugin?.id}`)
@@ -129,12 +131,10 @@ async function uninstallPlugin() {
// 通知父组件刷新 // 通知父组件刷新
emit('remove') emit('remove')
} } else {
else {
$toast.error(`插件 ${props.plugin?.plugin_name} 卸载失败:${result.message}}`) $toast.error(`插件 ${props.plugin?.plugin_name} 卸载失败:${result.message}}`)
} }
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -145,11 +145,9 @@ async function loadPluginForm() {
const result: { [key: string]: any } = await api.get(`plugin/form/${props.plugin?.id}`) const result: { [key: string]: any } = await api.get(`plugin/form/${props.plugin?.id}`)
if (result) { if (result) {
pluginFormItems = result.conf pluginFormItems = result.conf
if (result.model) if (result.model) pluginConfigForm.value = result.model
pluginConfigForm.value = result.model
} }
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -158,10 +156,8 @@ async function loadPluginForm() {
async function loadPluginPage() { async function loadPluginPage() {
try { try {
const result: [] = await api.get(`plugin/page/${props.plugin?.id}`) const result: [] = await api.get(`plugin/page/${props.plugin?.id}`)
if (result) if (result) pluginPageItems = result
pluginPageItems = result } catch (error) {
}
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -170,10 +166,8 @@ async function loadPluginPage() {
async function loadPluginConf() { async function loadPluginConf() {
try { try {
const result: { [key: string]: any } = await api.get(`plugin/${props.plugin?.id}`) const result: { [key: string]: any } = await api.get(`plugin/${props.plugin?.id}`)
if (!isNullOrEmptyObject(result)) if (!isNullOrEmptyObject(result)) pluginConfigForm.value = result
pluginConfigForm.value = result } catch (error) {
}
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -191,13 +185,11 @@ async function savePluginConf() {
$toast.success(`插件 ${props.plugin?.plugin_name} 配置已保存`) $toast.success(`插件 ${props.plugin?.plugin_name} 配置已保存`)
// 通知父组件刷新 // 通知父组件刷新
emit('save') emit('save')
} } else {
else {
progressDialog.value = false progressDialog.value = false
$toast.error(`插件 ${props.plugin?.plugin_name} 配置保存失败:${result.message}}`) $toast.error(`插件 ${props.plugin?.plugin_name} 配置保存失败:${result.message}}`)
} }
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -223,8 +215,7 @@ async function showPluginConfig() {
// 计算图标路径 // 计算图标路径
const iconPath: Ref<string> = computed(() => { const iconPath: Ref<string> = computed(() => {
if (imageLoadError.value) if (imageLoadError.value) return noImage
return noImage
// 如果是网络图片则使用代理后返回 // 如果是网络图片则使用代理后返回
if (props.plugin?.plugin_icon?.startsWith('http')) if (props.plugin?.plugin_icon?.startsWith('http'))
return `${import.meta.env.VITE_API_BASE_URL}system/img/1?imgurl=${encodeURIComponent(props.plugin?.plugin_icon)}` return `${import.meta.env.VITE_API_BASE_URL}system/img/1?imgurl=${encodeURIComponent(props.plugin?.plugin_icon)}`
@@ -247,8 +238,7 @@ async function resetPlugin() {
}, },
}) })
if (!isConfirmed) if (!isConfirmed) return
return
try { try {
const result: { [key: string]: any } = await api.get(`plugin/reset/${props.plugin?.id}`) const result: { [key: string]: any } = await api.get(`plugin/reset/${props.plugin?.id}`)
@@ -256,12 +246,10 @@ async function resetPlugin() {
$toast.success(`插件 ${props.plugin?.plugin_name} 数据已重置`) $toast.success(`插件 ${props.plugin?.plugin_name} 数据已重置`)
// 通知父组件刷新 // 通知父组件刷新
emit('save') emit('save')
} } else {
else {
$toast.error(`插件 ${props.plugin?.plugin_name} 重置失败:${result.message}}`) $toast.error(`插件 ${props.plugin?.plugin_name} 重置失败:${result.message}}`)
} }
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -274,15 +262,12 @@ async function updatePlugin() {
progressDialog.value = true progressDialog.value = true
progressText.value = `正在更新 ${props.plugin?.plugin_name} ...` progressText.value = `正在更新 ${props.plugin?.plugin_name} ...`
const result: { [key: string]: any } = await api.get( const result: { [key: string]: any } = await api.get(`plugin/install/${props.plugin?.id}`, {
`plugin/install/${props.plugin?.id}`, params: {
{ repo_url: props.plugin?.repo_url,
params: { force: true,
repo_url: props.plugin?.repo_url,
force: true,
},
}, },
) })
// 隐藏等待提示框 // 隐藏等待提示框
progressDialog.value = false progressDialog.value = false
@@ -292,12 +277,10 @@ async function updatePlugin() {
// 通知父组件刷新 // 通知父组件刷新
emit('save') emit('save')
} } else {
else {
$toast.error(`插件 ${props.plugin?.plugin_name} 更新失败:${result.message}`) $toast.error(`插件 ${props.plugin?.plugin_name} 更新失败:${result.message}`)
} }
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -310,16 +293,16 @@ function visitAuthorPage() {
// 查看日志URL // 查看日志URL
function openLoggerWindow() { function openLoggerWindow() {
const token = store.state.auth.token const token = store.state.auth.token
const url = `${import.meta.env.VITE_API_BASE_URL}system/logging?token=${token}&length=-1&logfile=plugins/${props.plugin?.id?.toLowerCase()}.log` const url = `${
import.meta.env.VITE_API_BASE_URL
}system/logging?token=${token}&length=-1&logfile=plugins/${props.plugin?.id?.toLowerCase()}.log`
window.open(url, '_blank') window.open(url, '_blank')
} }
// 打开插件详情 // 打开插件详情
function openPluginDetail() { function openPluginDetail() {
if (props.plugin?.has_page) if (props.plugin?.has_page) showPluginInfo()
showPluginInfo() else showPluginConfig()
else
showPluginConfig()
} }
// 弹出菜单 // 弹出菜单
@@ -395,41 +378,26 @@ const dropdownItems = ref([
]) ])
// 监听插件状态变化 // 监听插件状态变化
watch(() => props.plugin?.has_update, (newHasUpdate, oldHasUpdate) => { watch(
const updateItemIndex = dropdownItems.value.findIndex(item => item.value === 3) () => props.plugin?.has_update,
if (updateItemIndex !== -1) (newHasUpdate, oldHasUpdate) => {
dropdownItems.value[updateItemIndex].show = newHasUpdate const updateItemIndex = dropdownItems.value.findIndex(item => item.value === 3)
}) if (updateItemIndex !== -1) dropdownItems.value[updateItemIndex].show = newHasUpdate
},
)
</script> </script>
<template> <template>
<!-- 插件卡片 --> <!-- 插件卡片 -->
<VCard <VCard v-if="isVisible" :width="props.width" :height="props.height" @click="openPluginDetail">
v-if="isVisible" <div class="relative pa-4 text-center card-cover-blurred" :style="{ background: `${backgroundColor}` }">
:width="props.width" <div v-if="props.plugin?.has_update" class="me-n3 absolute top-0 left-1">
:height="props.height" <VIcon icon="mdi-new-box" class="text-white" />
@click="openPluginDetail"
>
<div
class="relative pa-4 text-center card-cover-blurred"
:style="{ background: `${backgroundColor}` }"
>
<div
v-if="props.plugin?.has_update"
class="me-n3 absolute top-0 left-1"
>
<VIcon
icon="mdi-new-box"
class="text-white"
/>
</div> </div>
<div class="me-n3 absolute top-0 right-3"> <div class="me-n3 absolute top-0 right-3">
<IconBtn> <IconBtn>
<VIcon icon="mdi-dots-vertical" class="text-white" /> <VIcon icon="mdi-dots-vertical" class="text-white" />
<VMenu <VMenu activator="parent" close-on-content-click>
activator="parent"
close-on-content-click
>
<VList> <VList>
<VListItem <VListItem
v-for="(item, i) in dropdownItems" v-for="(item, i) in dropdownItems"
@@ -448,9 +416,7 @@ watch(() => props.plugin?.has_update, (newHasUpdate, oldHasUpdate) => {
</VMenu> </VMenu>
</IconBtn> </IconBtn>
</div> </div>
<VAvatar <VAvatar size="8rem">
size="8rem"
>
<VImg <VImg
ref="imageRef" ref="imageRef"
:src="iconPath" :src="iconPath"
@@ -469,7 +435,8 @@ watch(() => props.plugin?.has_update, (newHasUpdate, oldHasUpdate) => {
<VCardItem class="py-2"> <VCardItem class="py-2">
<VCardTitle class="flex items-center flex-row"> <VCardTitle class="flex items-center flex-row">
<VBadge v-if="props.plugin?.state" dot inline color="success" class="me-1 mb-1" /> <VBadge v-if="props.plugin?.state" dot inline color="success" class="me-1 mb-1" />
{{ props.plugin?.plugin_name }}<span class="text-sm ms-2 mt-1 text-gray-500">v{{ props.plugin?.plugin_version }}</span> {{ props.plugin?.plugin_name
}}<span class="text-sm ms-2 mt-1 text-gray-500">v{{ props.plugin?.plugin_version }}</span>
</VCardTitle> </VCardTitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
@@ -477,110 +444,51 @@ watch(() => props.plugin?.has_update, (newHasUpdate, oldHasUpdate) => {
</VCardText> </VCardText>
</VCard> </VCard>
<!-- 插件配置页面 --> <!-- 插件配置页面 -->
<VDialog <VDialog v-model="pluginConfigDialog" scrollable max-width="60rem" :fullscreen="!display.mdAndUp.value">
v-model="pluginConfigDialog" <VCard :title="`${props.plugin?.plugin_name} - 配置`" class="rounded-t">
scrollable <DialogCloseBtn v-model="pluginConfigDialog" />
max-width="60rem"
:fullscreen="displayWidth < (60 * 16)"
>
<VCard
:title="`${props.plugin?.plugin_name} - 配置`"
class="rounded-t"
>
<DialogCloseBtn v-model='pluginConfigDialog' />
<VCardText> <VCardText>
<FormRender <FormRender v-for="(item, index) in pluginFormItems" :key="index" :config="item" :form="pluginConfigForm" />
v-for="(item, index) in pluginFormItems"
:key="index"
:config="item"
:form="pluginConfigForm"
/>
</VCardText> </VCardText>
<VCardActions> <VCardActions>
<VBtn v-if="pluginPageItems.length > 0" @click="showPluginInfo"> <VBtn v-if="pluginPageItems.length > 0" @click="showPluginInfo"> 查看数据 </VBtn>
查看数据
</VBtn>
<VSpacer /> <VSpacer />
<VBtn <VBtn variant="tonal" @click="savePluginConf"> 保存 </VBtn>
variant="tonal"
@click="savePluginConf"
>
保存
</VBtn>
</VCardActions> </VCardActions>
</VCard> </VCard>
</VDialog> </VDialog>
<!-- 插件数据页面 --> <!-- 插件数据页面 -->
<VDialog <VDialog v-model="pluginInfoDialog" scrollable max-width="80rem" :fullscreen="!display.mdAndUp.value">
v-model="pluginInfoDialog" <VCard :title="`${props.plugin?.plugin_name}`" class="rounded-t">
scrollable <DialogCloseBtn v-model="pluginInfoDialog" />
max-width="80rem"
:fullscreen="displayWidth < (80 * 16)"
>
<VCard
:title="`${props.plugin?.plugin_name}`"
class="rounded-t"
>
<DialogCloseBtn v-model='pluginInfoDialog' />
<VCardText> <VCardText>
<PageRender <PageRender v-for="(item, index) in pluginPageItems" :key="index" :config="item" />
v-for="(item, index) in pluginPageItems"
:key="index"
:config="item"
/>
</VCardText> </VCardText>
<VCardActions> <VCardActions>
<VBtn <VBtn @click="showPluginConfig"> 配置 </VBtn>
@click="showPluginConfig"
>
配置
</VBtn>
<VSpacer /> <VSpacer />
<VBtn <VBtn variant="tonal" @click="pluginInfoDialog = false"> 关闭 </VBtn>
variant="tonal"
@click="pluginInfoDialog = false"
>
关闭
</VBtn>
</VCardActions> </VCardActions>
</VCard> </VCard>
</VDialog> </VDialog>
<!-- 更新插件进度框 --> <!-- 更新插件进度框 -->
<VDialog <VDialog v-model="progressDialog" :scrim="false" width="25rem">
v-model="progressDialog" <VCard color="primary">
:scrim="false"
width="25rem"
>
<VCard
color="primary"
>
<VCardText class="text-center"> <VCardText class="text-center">
{{ progressText }} {{ progressText }}
<VProgressLinear <VProgressLinear indeterminate color="white" class="mb-0 mt-1" />
indeterminate
color="white"
class="mb-0 mt-1"
/>
</VCardText> </VCardText>
</VCard> </VCard>
</VDialog> </VDialog>
<!-- 更新日志 --> <!-- 更新日志 -->
<VDialog <VDialog v-if="releaseDialog" v-model="releaseDialog" width="600" scrollable>
v-if="releaseDialog"
v-model="releaseDialog"
width="600"
scrollable
>
<VCard> <VCard>
<DialogCloseBtn @click="releaseDialog = false" /> <DialogCloseBtn @click="releaseDialog = false" />
<VCardTitle>{{ props.plugin?.plugin_name }} 更新说明</VCardTitle> <VCardTitle>{{ props.plugin?.plugin_name }} 更新说明</VCardTitle>
<VersionHistory :history="props.plugin?.history" /> <VersionHistory :history="props.plugin?.history" />
<VCardText> <VCardText>
<VBtn <VBtn @click="updatePlugin" block>
@click="updatePlugin"
block
>
<template #prepend> <template #prepend>
<VIcon icon="mdi-arrow-up-circle-outline" /> <VIcon icon="mdi-arrow-up-circle-outline" />
</template> </template>
@@ -598,7 +506,7 @@ watch(() => props.plugin?.has_update, (newHasUpdate, oldHasUpdate) => {
-webkit-backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
backdrop-filter: blur(2px); backdrop-filter: blur(2px);
background: rgba(29, 39, 59, 48%); background: rgba(29, 39, 59, 48%);
content: ""; content: '';
inset: 0; inset: 0;
} }
</style> </style>
+55 -149
View File
@@ -11,7 +11,7 @@ import { isNullOrEmptyObject } from '@/@core/utils'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// 输入参数 // 输入参数
const cardProps = defineProps({ const cardProps = defineProps({
@@ -70,8 +70,7 @@ const siteStats = ref<SiteStatistic>({})
async function getSiteIcon() { async function getSiteIcon() {
try { try {
siteIcon.value = (await api.get(`site/icon/${cardProps.site?.id}`)).data.icon siteIcon.value = (await api.get(`site/icon/${cardProps.site?.id}`)).data.icon
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -83,17 +82,14 @@ async function testSite() {
testButtonDisable.value = true testButtonDisable.value = true
const result: { [key: string]: any } = await api.get(`site/test/${cardProps.site?.id}`) const result: { [key: string]: any } = await api.get(`site/test/${cardProps.site?.id}`)
if (result.success) if (result.success) $toast.success(`${cardProps.site?.name} 连通性测试成功,可正常使用!`)
$toast.success(`${cardProps.site?.name} 连通性测试成功,可正常使用!`) else $toast.error(`${cardProps.site?.name} 连通性测试失败:${result.message}`)
else
$toast.error(`${cardProps.site?.name} 连通性测试失败:${result.message}`)
testButtonText.value = '测试' testButtonText.value = '测试'
testButtonDisable.value = false testButtonDisable.value = false
getSiteStats() getSiteStats()
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -101,9 +97,8 @@ async function testSite() {
// 查询站点使用统计 // 查询站点使用统计
async function getSiteStats() { async function getSiteStats() {
try { try {
siteStats.value = (await api.get(`site/statistic/${cardProps.site?.domain}`)) siteStats.value = await api.get(`site/statistic/${cardProps.site?.domain}`)
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -121,8 +116,7 @@ async function handleResourceBrowse() {
// 调用API,更新站点Cookie UA // 调用API,更新站点Cookie UA
async function updateSiteCookie() { async function updateSiteCookie() {
try { try {
if (!userPwForm.value.username || !userPwForm.value.password) if (!userPwForm.value.username || !userPwForm.value.password) return
return
// 更新按钮状态 // 更新按钮状态
siteCookieDialog.value = false siteCookieDialog.value = false
@@ -131,26 +125,20 @@ async function updateSiteCookie() {
progressDialog.value = true progressDialog.value = true
progressText.value = `正在更新 ${cardProps.site?.name} Cookie & UA ...` progressText.value = `正在更新 ${cardProps.site?.name} Cookie & UA ...`
const result: { [key: string]: any } = await api.get( const result: { [key: string]: any } = await api.get(`site/cookie/${cardProps.site?.id}`, {
`site/cookie/${cardProps.site?.id}`, params: {
{ username: userPwForm.value.username,
params: { password: userPwForm.value.password,
username: userPwForm.value.username, code: userPwForm.value.code,
password: userPwForm.value.password,
code: userPwForm.value.code,
},
}, },
) })
if (result.success) if (result.success) $toast.success(`${cardProps.site?.name} 更新Cookie & UA 成功!`)
$toast.success(`${cardProps.site?.name} 更新Cookie & UA 成功!`) else $toast.error(`${cardProps.site?.name} 更新失败:${result.message}`)
else
$toast.error(`${cardProps.site?.name} 更新失败:${result.message}`)
progressDialog.value = false progressDialog.value = false
updateButtonDisable.value = false updateButtonDisable.value = false
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -162,25 +150,21 @@ function openSitePage() {
// 根据站点状态显示不同的状态图标 // 根据站点状态显示不同的状态图标
const statColor = computed(() => { const statColor = computed(() => {
if (isNullOrEmptyObject(siteStats.value)){ if (isNullOrEmptyObject(siteStats.value)) {
return 'secondary' return 'secondary'
} }
if (siteStats.value?.lst_state == 1){ if (siteStats.value?.lst_state == 1) {
return 'error' return 'error'
} } else if (siteStats.value?.lst_state == 0) {
else if (siteStats.value?.lst_state == 0){ if (!siteStats.value?.seconds) return 'secondary'
if (!siteStats.value?.seconds) if (siteStats.value?.seconds >= 5) return 'warning'
return 'secondary'
if (siteStats.value?.seconds >= 5)
return 'warning'
return 'success' return 'success'
} }
}) })
// 监听resourceDialog,如果为false则重新查询站点使用统计 // 监听resourceDialog,如果为false则重新查询站点使用统计
watch(resourceDialog, (value) => { watch(resourceDialog, value => {
if (!value) if (!value) getSiteStats()
getSiteStats()
}) })
// 装载时查询站点图标 // 装载时查询站点图标
@@ -199,11 +183,7 @@ onMounted(() => {
@click="siteEditDialog = true" @click="siteEditDialog = true"
> >
<template #image> <template #image>
<VAvatar <VAvatar class="absolute right-2 bottom-2 rounded" variant="flat" rounded="0">
class="absolute right-2 bottom-2 rounded"
variant="flat"
rounded="0"
>
<VImg :src="siteIcon" /> <VImg :src="siteIcon" />
</VAvatar> </VAvatar>
</template> </template>
@@ -220,83 +200,41 @@ onMounted(() => {
<StatIcon v-if="cardProps.site?.is_active" :color="statColor" /> <StatIcon v-if="cardProps.site?.is_active" :color="statColor" />
<VCardText class="py-2"> <VCardText class="py-2">
<VTooltip <VTooltip v-if="cardProps.site?.render === 1" text="浏览器仿真">
v-if="cardProps.site?.render === 1"
text="浏览器仿真"
>
<template #activator="{ props }"> <template #activator="{ props }">
<VIcon <VIcon color="primary" class="me-2" v-bind="props" icon="mdi-apple-safari" />
color="primary"
class="me-2"
v-bind="props"
icon="mdi-apple-safari"
/>
</template> </template>
</VTooltip> </VTooltip>
<VTooltip <VTooltip v-if="cardProps.site?.proxy === 1" text="代理">
v-if="cardProps.site?.proxy === 1"
text="代理"
>
<template #activator="{ props }"> <template #activator="{ props }">
<VIcon <VIcon color="primary" class="me-2" v-bind="props" icon="mdi-network-outline" />
color="primary"
class="me-2"
v-bind="props"
icon="mdi-network-outline"
/>
</template> </template>
</VTooltip> </VTooltip>
<VTooltip <VTooltip v-if="cardProps.site?.limit_interval" text="流控">
v-if="cardProps.site?.limit_interval"
text="流控"
>
<template #activator="{ props }"> <template #activator="{ props }">
<VIcon <VIcon color="primary" class="me-2" v-bind="props" icon="mdi-speedometer" />
color="primary"
class="me-2"
v-bind="props"
icon="mdi-speedometer"
/>
</template> </template>
</VTooltip> </VTooltip>
<VTooltip <VTooltip v-if="cardProps.site?.filter" text="过滤">
v-if="cardProps.site?.filter"
text="过滤"
>
<template #activator="{ props }"> <template #activator="{ props }">
<VIcon <VIcon color="primary" class="me-2" v-bind="props" icon="mdi-filter-cog-outline" />
color="primary"
class="me-2"
v-bind="props"
icon="mdi-filter-cog-outline"
/>
</template> </template>
</VTooltip> </VTooltip>
</VCardText> </VCardText>
<VDivider <VDivider class="opacity-75" style="border-color: rgba(var(--v-theme-on-background), var(--v-selected-opacity))" />
class="opacity-75"
style="border-color: rgba(var(--v-theme-on-background), var(--v-selected-opacity));"
/>
<VCardActions> <VCardActions>
<VBtn <VBtn v-if="!cardProps.site?.public" :disabled="updateButtonDisable" @click.stop="handleSiteUpdate">
v-if="!cardProps.site?.public"
:disabled="updateButtonDisable"
@click.stop="handleSiteUpdate"
>
<template #prepend> <template #prepend>
<VIcon icon="mdi-refresh" /> <VIcon icon="mdi-refresh" />
</template> </template>
更新 更新
</VBtn> </VBtn>
<VBtn <VBtn :disabled="testButtonDisable" @click.stop="testSite">
:disabled="testButtonDisable"
@click.stop="testSite"
>
<template #prepend> <template #prepend>
<VIcon icon="mdi-link" /> <VIcon icon="mdi-link" />
</template> </template>
@@ -311,51 +249,29 @@ onMounted(() => {
</VCardActions> </VCardActions>
</VCard> </VCard>
<!-- 更新站点Cookie & UA弹窗 --> <!-- 更新站点Cookie & UA弹窗 -->
<VDialog <VDialog v-model="siteCookieDialog" max-width="50rem" :fullscreen="!display.mdAndUp.value">
v-model="siteCookieDialog"
max-width="50rem"
:fullscreen="displayWidth < (50 * 16)"
>
<!-- Dialog Content --> <!-- Dialog Content -->
<VCard title="更新站点Cookie & UA"> <VCard title="更新站点Cookie & UA">
<DialogCloseBtn @click="siteCookieDialog=false" /> <DialogCloseBtn @click="siteCookieDialog = false" />
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<VRow> <VRow>
<VCol <VCol cols="12" md="4">
cols="12" <VTextField v-model="userPwForm.username" label="用户名" :rules="[requiredValidator]" />
md="4"
>
<VTextField
v-model="userPwForm.username"
label="用户名"
:rules="[requiredValidator]"
/>
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VTextField <VTextField
v-model="userPwForm.password" v-model="userPwForm.password"
label="密码" label="密码"
:type="isPasswordVisible ? 'text' : 'password'" :type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon=" :append-inner-icon="isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
"
:rules="[requiredValidator]" :rules="[requiredValidator]"
@click:append-inner="isPasswordVisible = !isPasswordVisible" @click:append-inner="isPasswordVisible = !isPasswordVisible"
@keydown.enter="updateSiteCookie" @keydown.enter="updateSiteCookie"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12" <VTextField v-model="userPwForm.code" label="两步验证" />
md="4"
>
<VTextField
v-model="userPwForm.code"
label="两步验证"
/>
</VCol> </VCol>
</VRow> </VRow>
</VForm> </VForm>
@@ -363,12 +279,7 @@ onMounted(() => {
<VCardActions> <VCardActions>
<VSpacer /> <VSpacer />
<VBtn <VBtn variant="tonal" @click="updateSiteCookie"> 开始更新 </VBtn>
variant="tonal"
@click="updateSiteCookie"
>
开始更新
</VBtn>
</VCardActions> </VCardActions>
</VCard> </VCard>
</VDialog> </VDialog>
@@ -376,7 +287,12 @@ onMounted(() => {
v-if="siteEditDialog" v-if="siteEditDialog"
v-model="siteEditDialog" v-model="siteEditDialog"
:siteid="cardProps.site?.id" :siteid="cardProps.site?.id"
@save="siteEditDialog = false; emit('update')" @save="
() => {
siteEditDialog = false
emit('update')
}
"
@remove="emit('remove')" @remove="emit('remove')"
@close="siteEditDialog = false" @close="siteEditDialog = false"
/> />
@@ -387,7 +303,7 @@ onMounted(() => {
max-width="80rem" max-width="80rem"
scrollable scrollable
z-index="1010" z-index="1010"
:fullscreen="displayWidth < (80 * 16)" :fullscreen="!display.mdAndUp.value"
> >
<!-- Dialog Content --> <!-- Dialog Content -->
<VCard :title="`浏览站点 - ${cardProps.site?.name}`"> <VCard :title="`浏览站点 - ${cardProps.site?.name}`">
@@ -397,21 +313,11 @@ onMounted(() => {
</VCardText> </VCardText>
</VCard> </VCard>
</VDialog> </VDialog>
<VDialog <VDialog v-model="progressDialog" :scrim="false" width="25rem">
v-model="progressDialog" <VCard color="primary">
:scrim="false"
width="25rem"
>
<VCard
color="primary"
>
<VCardText class="text-center"> <VCardText class="text-center">
{{ progressText }} {{ progressText }}
<VProgressLinear <VProgressLinear indeterminate color="white" class="mb-0 mt-1" />
indeterminate
color="white"
class="mb-0 mt-1"
/>
</VCardText> </VCardText>
</VCard> </VCard>
</VDialog> </VDialog>
+41 -83
View File
@@ -7,7 +7,7 @@ import { numberValidator } from '@/@validators'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
@@ -59,7 +59,6 @@ const transferForm = reactive({
episode_part: '', episode_part: '',
episode_offset: null, episode_offset: null,
min_filesize: 0, min_filesize: 0,
}) })
watchEffect(() => { watchEffect(() => {
@@ -76,7 +75,7 @@ function startLoadingProgress() {
progressEventSource.value = new EventSource( progressEventSource.value = new EventSource(
`${import.meta.env.VITE_API_BASE_URL}system/progress/filetransfer?token=${token}`, `${import.meta.env.VITE_API_BASE_URL}system/progress/filetransfer?token=${token}`,
) )
progressEventSource.value.onmessage = (event) => { progressEventSource.value.onmessage = event => {
const progress = JSON.parse(event.data) const progress = JSON.parse(event.data)
if (progress) { if (progress) {
progressText.value = progress.text progressText.value = progress.text
@@ -93,8 +92,7 @@ function stopLoadingProgress() {
// 整理文件 // 整理文件
// eslint-disable-next-line sonarjs/cognitive-complexity // eslint-disable-next-line sonarjs/cognitive-complexity
async function transfer() { async function transfer() {
if (!props.logids && !props.path) if (!props.logids && !props.path) return
return
// 显示进度条 // 显示进度条
progressDialog.value = true progressDialog.value = true
@@ -104,32 +102,33 @@ async function transfer() {
if (props.path) { if (props.path) {
// 文件整理 // 文件整理
try { try {
const result: { [key: string]: any } = await api.post('transfer/manual', {}, { const result: { [key: string]: any } = await api.post(
params: transferForm, 'transfer/manual',
}) {},
{
params: transferForm,
},
)
// 显示结果 // 显示结果
if (result.success) if (result.success) $toast.success(`${props.path} 整理完成!`)
$toast.success(`${props.path} 整理完成`) else $toast.error(`${props.path} 整理失败:${result.message}`)
} catch (e) {
else
$toast.error(`${props.path} 整理失败:${result.message}`)
}
catch (e) {
console.log(e) console.log(e)
} }
} } else if (props.logids) {
else if (props.logids) {
// 日志整理 // 日志整理
for (const logid of props.logids) { for (const logid of props.logids) {
transferForm.logid = logid transferForm.logid = logid
try { try {
const result: { [key: string]: any } = await api.post('transfer/manual', {}, { const result: { [key: string]: any } = await api.post(
params: transferForm, 'transfer/manual',
}) {},
if (!result.success) {
$toast.error(`历史记录 ${logid} 重新整理失败:${result.message}`) params: transferForm,
} },
catch (e) { )
if (!result.success) $toast.error(`历史记录 ${logid} 重新整理失败:${result.message}`)
} catch (e) {
console.log(e) console.log(e)
} }
} }
@@ -145,11 +144,7 @@ async function transfer() {
</script> </script>
<template> <template>
<VDialog <VDialog scrollable max-width="60rem" :fullscreen="!display.mdAndUp.value">
scrollable
max-width="60rem"
:fullscreen="displayWidth < (60 * 16)"
>
<VCard <VCard
:title="`${props.path ? `整理 - ${props.path}` : `整理 - 共 ${props.logids?.length} 条记录`}`" :title="`${props.path ? `整理 - ${props.path}` : `整理 - 共 ${props.logids?.length} 条记录`}`"
class="rounded-t" class="rounded-t"
@@ -158,10 +153,7 @@ async function transfer() {
<VCardText class="pt-2"> <VCardText class="pt-2">
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<VRow> <VRow>
<VCol <VCol cols="12" md="8">
cols="12"
md="8"
>
<VTextField <VTextField
v-model="transferForm.target" v-model="transferForm.target"
label="目的路径" label="目的路径"
@@ -169,10 +161,7 @@ async function transfer() {
hint="留空将自动整理到媒体库目录" hint="留空将自动整理到媒体库目录"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VSelect <VSelect
v-model="transferForm.transfer_type" v-model="transferForm.transfer_type"
label="整理方式" label="整理方式"
@@ -189,20 +178,18 @@ async function transfer() {
</VCol> </VCol>
</VRow> </VRow>
<VRow> <VRow>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VSelect <VSelect
v-model="transferForm.type_name" v-model="transferForm.type_name"
label="类型" label="类型"
:items="[{ title: '自动', value: '' }, { title: '电影', value: '电影' }, { title: '电视剧', value: '电视剧' }]" :items="[
{ title: '自动', value: '' },
{ title: '电影', value: '电影' },
{ title: '电视剧', value: '电视剧' },
]"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VTextField <VTextField
v-model="transferForm.tmdbid" v-model="transferForm.tmdbid"
:disabled="transferForm.type_name === ''" :disabled="transferForm.type_name === ''"
@@ -214,10 +201,7 @@ async function transfer() {
@click:append-inner="tmdbSelectorDialog = true" @click:append-inner="tmdbSelectorDialog = true"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VSelect <VSelect
v-show="transferForm.type_name === '电视剧'" v-show="transferForm.type_name === '电视剧'"
v-model.number="transferForm.season" v-model.number="transferForm.season"
@@ -272,49 +256,23 @@ async function transfer() {
</VForm> </VForm>
</VCardText> </VCardText>
<VCardActions> <VCardActions>
<VBtn depressed @click="emit('close')"> <VBtn depressed @click="emit('close')"> 取消 </VBtn>
取消
</VBtn>
<VSpacer /> <VSpacer />
<VBtn <VBtn variant="tonal" @click="transfer"> 开始整理 </VBtn>
variant="tonal"
@click="transfer"
>
开始整理
</VBtn>
</VCardActions> </VCardActions>
</VCard> </VCard>
<!-- 手动整理进度框 --> <!-- 手动整理进度框 -->
<VDialog <VDialog v-model="progressDialog" :scrim="false" width="25rem">
v-model="progressDialog" <VCard color="primary">
:scrim="false"
width="25rem"
>
<VCard
color="primary"
>
<VCardText class="text-center"> <VCardText class="text-center">
{{ progressText }} {{ progressText }}
<VProgressLinear <VProgressLinear v-if="progressValue" color="white" class="mb-0 mt-1" :model-value="progressValue" />
v-if="progressValue"
color="white"
class="mb-0 mt-1"
:model-value="progressValue"
/>
</VCardText> </VCardText>
</VCard> </VCard>
</VDialog> </VDialog>
<!-- TMDB ID搜索框 --> <!-- TMDB ID搜索框 -->
<VDialog <VDialog v-model="tmdbSelectorDialog" width="40rem" scrollable max-height="85vh">
v-model="tmdbSelectorDialog" <TmdbSelector v-model="transferForm.tmdbid" @close="tmdbSelectorDialog = false" />
width="40rem"
scrollable
max-height="85vh"
>
<TmdbSelector
v-model="transferForm.tmdbid"
@close="tmdbSelectorDialog = false"
/>
</VDialog> </VDialog>
</VDialog> </VDialog>
</template> </template>
+27 -98
View File
@@ -7,7 +7,7 @@ import api from '@/api'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
@@ -52,8 +52,7 @@ const priorityItems = ref(
// 监控输入参数 // 监控输入参数
watchEffect(async () => { watchEffect(async () => {
if (props.siteid) if (props.siteid) fetchSiteInfo()
fetchSiteInfo()
}) })
// 查询站点信息 // 查询站点信息
@@ -62,27 +61,24 @@ async function fetchSiteInfo() {
siteForm.value = await api.get(`site/${props.siteid}`) siteForm.value = await api.get(`site/${props.siteid}`)
siteForm.value.proxy = siteForm.value.proxy === 1 siteForm.value.proxy = siteForm.value.proxy === 1
siteForm.value.render = siteForm.value.render === 1 siteForm.value.render = siteForm.value.render === 1
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
// 调用API 新增站点 // 调用API 新增站点
async function addSite() { async function addSite() {
if (!siteForm.value?.url) if (!siteForm.value?.url) return
return
startNProgress() startNProgress()
try { try {
const result: { [key: string]: string } = await api.post('site/', siteForm.value) const result: { [key: string]: string } = await api.post('site/', siteForm.value)
if (result.success) { if (result.success) {
$toast.success('新增站点成功') $toast.success('新增站点成功')
emit('save') emit('save')
} else {
$toast.error(`新增站点失败:${result.message}`)
} }
} catch (error) {
else { $toast.error(`新增站点失败:${result.message}`) }
}
catch (error) {
console.error(error) console.error(error)
} }
doneNProgress() doneNProgress()
@@ -92,12 +88,9 @@ async function addSite() {
async function deleteSiteInfo() { async function deleteSiteInfo() {
try { try {
const result: { [key: string]: any } = await api.delete(`site/${siteForm.value?.id}`) const result: { [key: string]: any } = await api.delete(`site/${siteForm.value?.id}`)
if (result.success) if (result.success) emit('remove')
emit('remove')
else $toast.error(`${siteForm.value?.name} 删除失败:${result.message}`) else $toast.error(`${siteForm.value?.name} 删除失败:${result.message}`)
} } catch (error) {
catch (error) {
$toast.error(`${siteForm.value?.name} 删除失败!`) $toast.error(`${siteForm.value?.name} 删除失败!`)
console.error(error) console.error(error)
} }
@@ -111,10 +104,10 @@ async function updateSiteInfo() {
if (result.success) { if (result.success) {
$toast.success(`${siteForm.value?.name} 更新成功!`) $toast.success(`${siteForm.value?.name} 更新成功!`)
emit('save') emit('save')
} else {
$toast.error(`${siteForm.value?.name} 更新失败:${result.message}`)
} }
else { $toast.error(`${siteForm.value?.name} 更新失败:${result.message}`) } } catch (error) {
}
catch (error) {
$toast.error(`${siteForm.value?.name} 更新失败!`) $toast.error(`${siteForm.value?.name} 更新失败!`)
console.error(error) console.error(error)
} }
@@ -123,14 +116,7 @@ async function updateSiteInfo() {
</script> </script>
<template> <template>
<VDialog <VDialog scrollable :close-on-back="false" persistent eager max-width="60rem" :fullscreen="!display.mdAndUp.value">
scrollable
:close-on-back="false"
persistent
eager
max-width="60rem"
:fullscreen="displayWidth < (60 * 16)"
>
<VCard <VCard
:title="`${props.oper === 'add' ? '新增' : '编辑'}站点${props.oper !== 'add' ? ` - ${siteForm.name}` : ''}`" :title="`${props.oper === 'add' ? '新增' : '编辑'}站点${props.oper !== 'add' ? ` - ${siteForm.name}` : ''}`"
class="rounded-t" class="rounded-t"
@@ -139,10 +125,7 @@ async function updateSiteInfo() {
<VCardText class="pt-2"> <VCardText class="pt-2">
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<VRow> <VRow>
<VCol <VCol cols="12" md="6">
cols="12"
md="6"
>
<VTextField <VTextField
v-model="siteForm.url" v-model="siteForm.url"
label="站点地址" label="站点地址"
@@ -150,10 +133,7 @@ async function updateSiteInfo() {
hint="格式:http://www.example.com/" hint="格式:http://www.example.com/"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="3">
cols="12"
md="3"
>
<VSelect <VSelect
v-model="siteForm.pri" v-model="siteForm.pri"
label="优先级" label="优先级"
@@ -162,15 +142,8 @@ async function updateSiteInfo() {
hint="站点资源下载优先级,优先级数字越小越优先下载" hint="站点资源下载优先级,优先级数字越小越优先下载"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="3">
cols="12" <VSelect v-model="siteForm.is_active" :items="statusItems" label="状态" />
md="3"
>
<VSelect
v-model="siteForm.is_active"
:items="statusItems"
label="状态"
/>
</VCol> </VCol>
</VRow> </VRow>
<VRow> <VRow>
@@ -197,10 +170,7 @@ async function updateSiteInfo() {
</VCol> </VCol>
</VRow> </VRow>
<VRow> <VRow>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VTextField <VTextField
v-model="siteForm.limit_interval" v-model="siteForm.limit_interval"
label="单位周期(秒)" label="单位周期(秒)"
@@ -208,10 +178,7 @@ async function updateSiteInfo() {
hint="设定站点限流的单位周期,单位为秒,0为不限流" hint="设定站点限流的单位周期,单位为秒,0为不限流"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VTextField <VTextField
v-model="siteForm.limit_count" v-model="siteForm.limit_count"
label="访问次数" label="访问次数"
@@ -219,10 +186,7 @@ async function updateSiteInfo() {
hint="设定单位周期内站点允许的访问次数,0为不限制" hint="设定单位周期内站点允许的访问次数,0为不限制"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VTextField <VTextField
v-model="siteForm.limit_seconds" v-model="siteForm.limit_seconds"
label="访问间隔(秒)" label="访问间隔(秒)"
@@ -232,20 +196,10 @@ async function updateSiteInfo() {
</VCol> </VCol>
</VRow> </VRow>
<VRow> <VRow>
<VCol <VCol cols="12" md="6">
cols="12" <VSwitch v-model="siteForm.proxy" label="代理" hint="站点是否需要代理访问,需要设置好代理服务器信息" />
md="6"
>
<VSwitch
v-model="siteForm.proxy"
label="代理"
hint="站点是否需要代理访问,需要设置好代理服务器信息"
/>
</VCol> </VCol>
<VCol <VCol cols="12" md="6">
cols="12"
md="6"
>
<VSwitch <VSwitch
v-model="siteForm.render" v-model="siteForm.render"
label="仿真" label="仿真"
@@ -256,36 +210,11 @@ async function updateSiteInfo() {
</VForm> </VForm>
</VCardText> </VCardText>
<VCardActions> <VCardActions>
<VBtn <VBtn v-if="props.oper === 'add'" @click="emit('close')"> 取消 </VBtn>
v-if="props.oper === 'add'" <VBtn v-else color="error" @click="deleteSiteInfo"> 删除 </VBtn>
@click="emit('close')"
>
取消
</VBtn>
<VBtn
v-else
color="error"
@click="deleteSiteInfo"
>
删除
</VBtn>
<VSpacer /> <VSpacer />
<VBtn <VBtn v-if="props.oper === 'add'" color="primary" variant="tonal" @click="addSite"> 新增 </VBtn>
v-if="props.oper === 'add'" <VBtn v-else color="primary" variant="tonal" @click="updateSiteInfo"> 保存 </VBtn>
color="primary"
variant="tonal"
@click="addSite"
>
新增
</VBtn>
<VBtn
v-else
color="primary"
variant="tonal"
@click="updateSiteInfo"
>
保存
</VBtn>
</VCardActions> </VCardActions>
</VCard> </VCard>
</VDialog> </VDialog>
+48 -126
View File
@@ -6,7 +6,7 @@ import type { Site, Subscribe } from '@/api/types'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
@@ -48,7 +48,7 @@ const subscribeForm = ref<Subscribe>({
current_priority: 0, current_priority: 0,
save_path: '', save_path: '',
date: '', date: '',
show_edit_dialog: false show_edit_dialog: false,
}) })
// 提示框 // 提示框
@@ -63,10 +63,10 @@ async function updateSubscribeInfo() {
$toast.success(`${subscribeForm.value.name} 更新成功!`) $toast.success(`${subscribeForm.value.name} 更新成功!`)
// 通知父组件刷新 // 通知父组件刷新
emit('save') emit('save')
} else {
$toast.error(`${subscribeForm.value.name} 更新失败:${result.message}`)
} }
else { $toast.error(`${subscribeForm.value.name} 更新失败:${result.message}`) } } catch (e) {
}
catch (e) {
console.log(e) console.log(e)
} }
} }
@@ -75,23 +75,16 @@ async function updateSubscribeInfo() {
async function saveDefaultSubscribeConfig() { async function saveDefaultSubscribeConfig() {
try { try {
let subscribe_config_url = '' let subscribe_config_url = ''
if (props.type === '电影') if (props.type === '电影') subscribe_config_url = 'system/setting/DefaultMovieSubscribeConfig'
subscribe_config_url = 'system/setting/DefaultMovieSubscribeConfig' else subscribe_config_url = 'system/setting/DefaultTvSubscribeConfig'
else
subscribe_config_url = 'system/setting/DefaultTvSubscribeConfig'
const result: { [key: string]: any } = await api.post( const result: { [key: string]: any } = await api.post(subscribe_config_url, subscribeForm.value)
subscribe_config_url, if (result.success) $toast.success(`${props.type}订阅默认规则保存成功`)
subscribeForm.value) else $toast.error(`${props.type}订阅默认规则保存失败!`)
if (result.success)
$toast.success(`${props.type}订阅默认规则保存成功`)
else
$toast.error(`${props.type}订阅默认规则保存失败!`)
// 通知父组件刷新 // 通知父组件刷新
emit('save') emit('save')
} } catch (error) {
catch (error) {
console.log(error) console.log(error)
} }
} }
@@ -100,17 +93,13 @@ async function saveDefaultSubscribeConfig() {
async function queryDefaultSubscribeConfig() { async function queryDefaultSubscribeConfig() {
try { try {
let subscribe_config_url = '' let subscribe_config_url = ''
if (props.type === '电影') if (props.type === '电影') subscribe_config_url = 'system/setting/DefaultMovieSubscribeConfig'
subscribe_config_url = 'system/setting/DefaultMovieSubscribeConfig' else subscribe_config_url = 'system/setting/DefaultTvSubscribeConfig'
else
subscribe_config_url = 'system/setting/DefaultTvSubscribeConfig'
const result: { [key: string]: any } = await api.get(subscribe_config_url) const result: { [key: string]: any } = await api.get(subscribe_config_url)
if (result.data.value) if (result.data.value) subscribeForm.value = result.data?.value ?? ''
subscribeForm.value = result.data?.value ?? '' } catch (error) {
}
catch (error) {
console.log(error) console.log(error)
} }
} }
@@ -122,8 +111,7 @@ async function loadSites() {
// 过滤站点,只有启用的站点才显示 // 过滤站点,只有启用的站点才显示
siteList.value = data.filter(item => item.is_active) siteList.value = data.filter(item => item.is_active)
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
@@ -131,10 +119,9 @@ async function loadSites() {
// 获取站点列表选择框数据 // 获取站点列表选择框数据
async function getSiteList() { async function getSiteList() {
// 加载订阅站点列表 // 加载订阅站点列表
if (!siteList.value.length) if (!siteList.value.length) await loadSites()
await loadSites()
const maps = siteList.value.map((item) => { const maps = siteList.value.map(item => {
return { return {
title: item.name, title: item.name,
value: item.id, value: item.id,
@@ -147,14 +134,11 @@ async function getSiteList() {
// 获取订阅信息 // 获取订阅信息
async function getSubscribeInfo() { async function getSubscribeInfo() {
try { try {
const result: Subscribe = await api.get( const result: Subscribe = await api.get(`subscribe/${props.subid}`)
`subscribe/${props.subid}`,
)
subscribeForm.value = result subscribeForm.value = result
subscribeForm.value.best_version = subscribeForm.value.best_version === 1 subscribeForm.value.best_version = subscribeForm.value.best_version === 1
subscribeForm.value.search_imdbid = subscribeForm.value.search_imdbid === 1 subscribeForm.value.search_imdbid = subscribeForm.value.search_imdbid === 1
} } catch (e) {
catch (e) {
console.log(e) console.log(e)
} }
} }
@@ -162,16 +146,13 @@ async function getSubscribeInfo() {
// 删除订阅 // 删除订阅
async function removeSubscribe() { async function removeSubscribe() {
try { try {
const result: { [key: string]: any } = await api.delete( const result: { [key: string]: any } = await api.delete(`subscribe/${props.subid}`)
`subscribe/${props.subid}`,
)
if (result.success) { if (result.success) {
// 通知父组件刷新 // 通知父组件刷新
emit('remove') emit('remove')
} }
} } catch (e) {
catch (e) {
console.log(e) console.log(e)
} }
} }
@@ -262,32 +243,27 @@ const effectOptions = ref([
onMounted(() => { onMounted(() => {
getSiteList() getSiteList()
if (props.subid) if (props.subid) getSubscribeInfo()
getSubscribeInfo()
if (props.default) if (props.default) queryDefaultSubscribeConfig()
queryDefaultSubscribeConfig()
}) })
</script> </script>
<template> <template>
<VDialog <VDialog scrollable max-width="60rem" :fullscreen="!display.mdAndUp.value">
scrollable
max-width="60rem"
:fullscreen="displayWidth < (60 * 16)"
>
<VCard <VCard
:title="`${props.default ? `${props.type}默认订阅规则` : `编辑订阅 - ${subscribeForm.name} ${subscribeForm.season ? `第 ${subscribeForm.season} 季` : ''}`}`" :title="`${
props.default
? `${props.type}默认订阅规则`
: `编辑订阅 - ${subscribeForm.name} ${subscribeForm.season ? `第 ${subscribeForm.season} 季` : ''}`
}`"
class="rounded-t" class="rounded-t"
> >
<VCardText class="pt-2"> <VCardText class="pt-2">
<DialogCloseBtn @click="emit('close')" /> <DialogCloseBtn @click="emit('close')" />
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<VRow> <VRow>
<VCol <VCol cols="12" md="8">
cols="12"
md="8"
>
<VTextField <VTextField
v-if="!props.default" v-if="!props.default"
v-model="subscribeForm.keyword" v-model="subscribeForm.keyword"
@@ -295,11 +271,7 @@ onMounted(() => {
hint="设定搜索关键词后,将使用此关键词搜索站点资源,否则自动使用themoviedb中的名称搜索" hint="设定搜索关键词后,将使用此关键词搜索站点资源,否则自动使用themoviedb中的名称搜索"
/> />
</VCol> </VCol>
<VCol <VCol v-if="subscribeForm.type === '电视剧'" cols="12" md="2">
v-if="subscribeForm.type === '电视剧'"
cols="12"
md="2"
>
<VTextField <VTextField
v-model="subscribeForm.total_episode" v-model="subscribeForm.total_episode"
label="总集数" label="总集数"
@@ -307,11 +279,7 @@ onMounted(() => {
hint="设定剧集的总集数,以应对themoviedb中剧集信息未维护完整,导致提前结束订阅的情况" hint="设定剧集的总集数,以应对themoviedb中剧集信息未维护完整,导致提前结束订阅的情况"
/> />
</VCol> </VCol>
<VCol <VCol v-if="subscribeForm.type === '电视剧'" cols="12" md="2">
v-if="subscribeForm.type === '电视剧'"
cols="12"
md="2"
>
<VTextField <VTextField
v-model="subscribeForm.start_episode" v-model="subscribeForm.start_episode"
label="开始集数" label="开始集数"
@@ -321,62 +289,32 @@ onMounted(() => {
</VCol> </VCol>
</VRow> </VRow>
<VRow> <VRow>
<VCol <VCol cols="12" md="4">
cols="12" <VSelect v-model="subscribeForm.quality" label="质量" :items="qualityOptions" />
md="4"
>
<VSelect
v-model="subscribeForm.quality"
label="质量"
:items="qualityOptions"
/>
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12" <VSelect v-model="subscribeForm.resolution" label="分辨率" :items="resolutionOptions" />
md="4"
>
<VSelect
v-model="subscribeForm.resolution"
label="分辨率"
:items="resolutionOptions"
/>
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12" <VSelect v-model="subscribeForm.effect" label="特效" :items="effectOptions" />
md="4"
>
<VSelect
v-model="subscribeForm.effect"
label="特效"
:items="effectOptions"
/>
</VCol> </VCol>
</VRow> </VRow>
<VRow> <VRow>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VTextField <VTextField
v-model="subscribeForm.include" v-model="subscribeForm.include"
label="包含(关键字、正则式)" label="包含(关键字、正则式)"
hint="支持正则表达式,多个关键字用 | 分隔表示或" hint="支持正则表达式,多个关键字用 | 分隔表示或"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VTextField <VTextField
v-model="subscribeForm.exclude" v-model="subscribeForm.exclude"
label="排除(关键字、正则式)" label="排除(关键字、正则式)"
hint="支持正则表达式,多个关键字用 | 分隔表示或" hint="支持正则表达式,多个关键字用 | 分隔表示或"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VSelect <VSelect
v-model="subscribeForm.sites" v-model="subscribeForm.sites"
:items="selectSitesOptions" :items="selectSitesOptions"
@@ -388,9 +326,7 @@ onMounted(() => {
</VCol> </VCol>
</VRow> </VRow>
<VRow> <VRow>
<VCol <VCol cols="12">
cols="12"
>
<VTextField <VTextField
v-model="subscribeForm.save_path" v-model="subscribeForm.save_path"
label="保存路径" label="保存路径"
@@ -399,30 +335,21 @@ onMounted(() => {
</VCol> </VCol>
</VRow> </VRow>
<VRow> <VRow>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VSwitch <VSwitch
v-model="subscribeForm.best_version" v-model="subscribeForm.best_version"
label="洗版" label="洗版"
hint="开启后不管媒体库是否存在,均会根据洗版优先级进行过滤下载,直到下载到了最高优先级的资源为止" hint="开启后不管媒体库是否存在,均会根据洗版优先级进行过滤下载,直到下载到了最高优先级的资源为止"
/> />
</VCol> </VCol>
<VCol <VCol cols="12" md="4">
cols="12"
md="4"
>
<VSwitch <VSwitch
v-model="subscribeForm.search_imdbid" v-model="subscribeForm.search_imdbid"
label="使用 ImdbID 搜索" label="使用 ImdbID 搜索"
hint="开启后将使用 ImdbID 搜索资源,搜索结果更精确,但不是所有站点都支持" hint="开启后将使用 ImdbID 搜索资源,搜索结果更精确,但不是所有站点都支持"
/> />
</VCol> </VCol>
<VCol v-if="props.default" <VCol v-if="props.default" cols="12" md="4">
cols="12"
md="4"
>
<VSwitch <VSwitch
v-model="subscribeForm.show_edit_dialog" v-model="subscribeForm.show_edit_dialog"
label="订阅时编辑更多规则" label="订阅时编辑更多规则"
@@ -434,14 +361,9 @@ onMounted(() => {
</VCardText> </VCardText>
<VCardActions> <VCardActions>
<VBtn v-if="!props.default" color="error" @click="removeSubscribe"> <VBtn v-if="!props.default" color="error" @click="removeSubscribe"> 取消订阅 </VBtn>
取消订阅
</VBtn>
<VSpacer /> <VSpacer />
<VBtn <VBtn variant="tonal" @click=";`${props.default ? saveDefaultSubscribeConfig() : updateSubscribeInfo()}`">
variant="tonal"
@click="`${props.default ? saveDefaultSubscribeConfig() : updateSubscribeInfo()}`"
>
保存 保存
</VBtn> </VBtn>
</VCardActions> </VCardActions>
@@ -1,11 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import api from '@/api'; import api from '@/api'
import { Subscribe } from '@/api/types'; import { Subscribe } from '@/api/types'
import { formatDateDifference } from '@core/utils/formatters' import { formatDateDifference } from '@core/utils/formatters'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
@@ -81,14 +81,12 @@ async function loadHistory({ done }: { done: any }) {
// 重新订阅 // 重新订阅
async function reSubscribe(item: Subscribe) { async function reSubscribe(item: Subscribe) {
if (item.type === '电影') if (item.type === '电影') progressText.value = `正在重新订阅 ${item.name} ...`
progressText.value = `正在重新订阅 ${item.name} ...` else progressText.value = `正在重新订阅 ${item.name}${item.season} ...`
else
progressText.value = `正在重新订阅 ${item.name}${item.season} 季 ...`
progressDialog.value = true progressDialog.value = true
try { try {
const result: {[key: string]: any} = await api.post('subscribe', item) const result: { [key: string]: any } = await api.post('subscribe', item)
if (result.success){ if (result.success) {
emit('save') emit('save')
} }
} catch (e) { } catch (e) {
@@ -100,9 +98,9 @@ async function reSubscribe(item: Subscribe) {
// 删除记录 // 删除记录
async function deleteHistory(item: Subscribe) { async function deleteHistory(item: Subscribe) {
try { try {
const result: {[key: string]: any} = await api.delete(`subscribe/history/${item.id}`) const result: { [key: string]: any } = await api.delete(`subscribe/history/${item.id}`)
if (result.success){ if (result.success) {
historyList.value = historyList.value.filter((i) => i.id !== item.id) historyList.value = historyList.value.filter(i => i.id !== item.id)
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e)
@@ -128,35 +126,25 @@ const dropdownItems = ref([
prependIcon: 'mdi-delete', prependIcon: 'mdi-delete',
click: deleteHistory, click: deleteHistory,
}, },
} },
]) ])
</script> </script>
<template> <template>
<VDialog <VDialog scrollable max-width="50rem" :fullscreen="!display.mdAndUp.value">
scrollable <VCard class="mx-auto" width="100%">
max-width="50rem"
:fullscreen="displayWidth < (50 * 16)"
>
<VCard
class="mx-auto"
width="100%"
>
<VCardItem class="pb-0"> <VCardItem class="pb-0">
<VCardTitle>{{ props.type + '订阅历史' }}</VCardTitle> <VCardTitle>{{ props.type + '订阅历史' }}</VCardTitle>
</VCardItem> </VCardItem>
<DialogCloseBtn @click="() => { emit('close') }" /> <DialogCloseBtn
<VList @click="
lines="two" () => {
> emit('close')
<VInfiniteScroll }
mode="intersect" "
side="end" />
:items="historyList" <VList lines="two">
class="overflow-hidden" <VInfiniteScroll mode="intersect" side="end" :items="historyList" class="overflow-hidden" @load="loadHistory">
@load="loadHistory"
>
<template #loading> <template #loading>
<LoadingBanner /> <LoadingBanner />
</template> </template>
@@ -190,13 +178,8 @@ const dropdownItems = ref([
<template #append> <template #append>
<div class="me-n3"> <div class="me-n3">
<IconBtn> <IconBtn>
<VIcon <VIcon icon="mdi-dots-vertical" />
icon="mdi-dots-vertical" <VMenu activator="parent" close-on-content-click>
/>
<VMenu
activator="parent"
close-on-content-click
>
<VList> <VList>
<VListItem <VListItem
v-for="(menu, i) in dropdownItems" v-for="(menu, i) in dropdownItems"
@@ -221,21 +204,11 @@ const dropdownItems = ref([
</VList> </VList>
</VCard> </VCard>
<!-- 进度框 --> <!-- 进度框 -->
<VDialog <VDialog v-model="progressDialog" :scrim="false" width="25rem">
v-model="progressDialog" <VCard color="primary">
:scrim="false"
width="25rem"
>
<VCard
color="primary"
>
<VCardText class="text-center"> <VCardText class="text-center">
{{ progressText }} {{ progressText }}
<VProgressLinear <VProgressLinear indeterminate color="white" class="mb-0 mt-1" />
indeterminate
color="white"
class="mb-0 mt-1"
/>
</VCardText> </VCardText>
</VCard> </VCard>
</VDialog> </VDialog>
+5 -11
View File
@@ -10,7 +10,7 @@ import api from '@/api'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// App捷径 // App捷径
const appsMenu = ref(false) const appsMenu = ref(false)
@@ -179,7 +179,7 @@ onMounted(() => {
</VCard> </VCard>
</VDialog> </VDialog>
<!-- 网络测试弹窗 --> <!-- 网络测试弹窗 -->
<VDialog v-if="netTestDialog" v-model="netTestDialog" max-width="35rem" scrollable> <VDialog v-if="netTestDialog" v-model="netTestDialog" max-width="35rem" max-height="85vh" scrollable>
<VCard title="网络测试"> <VCard title="网络测试">
<DialogCloseBtn @click="netTestDialog = false" /> <DialogCloseBtn @click="netTestDialog = false" />
<VCardText> <VCardText>
@@ -193,7 +193,7 @@ onMounted(() => {
v-model="loggingDialog" v-model="loggingDialog"
scrollable scrollable
max-width="70rem" max-width="70rem"
:fullscreen="displayWidth < 70 * 16" :fullscreen="!display.mdAndUp.value"
> >
<VCard> <VCard>
<DialogCloseBtn @click="loggingDialog = false" /> <DialogCloseBtn @click="loggingDialog = false" />
@@ -225,13 +225,7 @@ onMounted(() => {
</VCard> </VCard>
</VDialog> </VDialog>
<!-- 系统健康检查弹窗 --> <!-- 系统健康检查弹窗 -->
<VDialog <VDialog v-if="systemTestDialog" v-model="systemTestDialog" max-width="35rem" max-height="85vh" scrollable>
v-if="systemTestDialog"
v-model="systemTestDialog"
max-width="50rem"
scrollable
:fullscreen="displayWidth < 50 * 16"
>
<VCard title="系统健康检查"> <VCard title="系统健康检查">
<DialogCloseBtn @click="systemTestDialog = false" /> <DialogCloseBtn @click="systemTestDialog = false" />
<VCardText> <VCardText>
@@ -245,7 +239,7 @@ onMounted(() => {
v-model="messageDialog" v-model="messageDialog"
max-width="60rem" max-width="60rem"
scrollable scrollable
:fullscreen="displayWidth < 60 * 16" :fullscreen="!display.mdAndUp.value"
> >
<VCard title="消息中心"> <VCard title="消息中心">
<DialogCloseBtn @click="messageDialog = false" /> <DialogCloseBtn @click="messageDialog = false" />
+20 -90
View File
@@ -14,7 +14,7 @@ import { isNullOrEmptyObject } from '@/@core/utils'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// 仪表盘配置 // 仪表盘配置
const dashboard_names = { const dashboard_names = {
@@ -60,8 +60,8 @@ function setDashboardConfig() {
// 保存到服务端 // 保存到服务端
api.post('/user/config/Dashboard', data, { api.post('/user/config/Dashboard', data, {
headers: { headers: {
"Content-Type": "application/json" 'Content-Type': 'application/json',
} },
}) })
dialog.value = false dialog.value = false
} }
@@ -69,133 +69,63 @@ function setDashboardConfig() {
<template> <template>
<!-- 底部操作按钮 --> <!-- 底部操作按钮 -->
<VFab <VFab icon="mdi-view-dashboard-edit" location="bottom end" size="x-large" fixed app appear @click="dialog = true" />
icon="mdi-view-dashboard-edit"
location="bottom end"
size="x-large"
fixed
app
appear
@click="dialog = true"
/>
<VRow class="match-height"> <VRow class="match-height">
<VCol <VCol v-if="config.storage" cols="12" md="4">
v-if="config.storage"
cols="12"
md="4"
>
<AnalyticsStorage /> <AnalyticsStorage />
</VCol> </VCol>
<VCol <VCol v-if="config.mediaStatistic" cols="12" md="8">
v-if="config.mediaStatistic"
cols="12"
md="8"
>
<AnalyticsMediaStatistic /> <AnalyticsMediaStatistic />
</VCol> </VCol>
<VCol <VCol v-if="config.weeklyOverview" cols="12" md="4">
v-if="config.weeklyOverview"
cols="12"
md="4"
>
<AnalyticsWeeklyOverview /> <AnalyticsWeeklyOverview />
</VCol> </VCol>
<VCol <VCol v-if="config.speed" cols="12" md="4">
v-if="config.speed"
cols="12"
md="4"
>
<AnalyticsSpeed /> <AnalyticsSpeed />
</VCol> </VCol>
<VCol <VCol v-if="config.scheduler" cols="12" md="4">
v-if="config.scheduler"
cols="12"
md="4"
>
<AnalyticsScheduler /> <AnalyticsScheduler />
</VCol> </VCol>
<VCol <VCol v-if="config.cpu" cols="12" md="6">
v-if="config.cpu"
cols="12"
md="6"
>
<AnalyticsCpu /> <AnalyticsCpu />
</VCol> </VCol>
<VCol <VCol v-if="config.memory" cols="12" md="6">
v-if="config.memory"
cols="12"
md="6"
>
<AnalyticsMemory /> <AnalyticsMemory />
</VCol> </VCol>
<VCol <VCol v-if="config.library" cols="12">
v-if="config.library"
cols="12"
>
<MediaServerLibrary /> <MediaServerLibrary />
</VCol> </VCol>
<VCol <VCol v-if="config.playing" cols="12">
v-if="config.playing"
cols="12"
>
<MediaServerPlaying /> <MediaServerPlaying />
</VCol> </VCol>
<VCol <VCol v-if="config.latest" cols="12">
v-if="config.latest"
cols="12"
>
<MediaServerLatest /> <MediaServerLatest />
</VCol> </VCol>
</VRow> </VRow>
<!-- 弹窗根据配置生成选项 --> <!-- 弹窗根据配置生成选项 -->
<VDialog <VDialog v-model="dialog" max-width="40rem" scrollable :fullscreen="!display.mdAndUp.value">
v-model="dialog"
max-width="600"
scrollable
:fullscreen="displayWidth < 600"
>
<VCard title="设置仪表板"> <VCard title="设置仪表板">
<VCardText> <VCardText>
<VRow> <VRow>
<VCol <VCol v-for="(item, key) in dashboard_names" :key="key" cols="12" md="4" sm="4">
v-for="(item, key) in dashboard_names" <VCheckbox v-model="config[key]" :label="dashboard_names[key]" />
:key="key"
cols="12"
md="4"
sm="4"
>
<VCheckbox
v-model="config[key]"
:label="dashboard_names[key]"
/>
</VCol> </VCol>
</VRow> </VRow>
</VCardText> </VCardText>
<VCardActions> <VCardActions>
<VBtn <VBtn color="primary" @click="dialog = false"> 取消 </VBtn>
color="primary"
@click="dialog = false"
>
取消
</VBtn>
<VSpacer /> <VSpacer />
<VBtn <VBtn color="primary" variant="tonal" @click="setDashboardConfig"> 保存 </VBtn>
color="primary"
variant="tonal"
@click="setDashboardConfig"
>
保存
</VBtn>
</VCardActions> </VCardActions>
</VCard> </VCard>
</vdialog> </VDialog>
</template> </template>
+3 -3
View File
@@ -9,7 +9,7 @@ import noImage from '@images/logos/plugin.png'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
// 显示器宽度 // 显示器宽度
const displayWidth = useDisplay().width const display = useDisplay()
// 已安装插件列表 // 已安装插件列表
const dataList = ref<Plugin[]>([]) const dataList = ref<Plugin[]>([])
@@ -289,8 +289,8 @@ onBeforeMount(() => {
scrollable scrollable
:z-index="1010" :z-index="1010"
max-width="40rem" max-width="40rem"
:max-height="displayWidth < 40 * 16 ? '' : '85vh'" :max-height="!display.mdAndUp.value ? '' : '85vh'"
:fullscreen="displayWidth < 40 * 16" :fullscreen="!display.mdAndUp.value"
> >
<VCard class="mx-auto" width="100%"> <VCard class="mx-auto" width="100%">
<VToolbar flat class="p-0"> <VToolbar flat class="p-0">
@@ -62,6 +62,7 @@ const pageRange = [
{ title: '25', value: 25 }, { title: '25', value: 25 },
{ title: '50', value: 50 }, { title: '50', value: 50 },
{ title: '100', value: 100 }, { title: '100', value: 100 },
{ title: '500', value: 500 },
{ title: '1000', value: 1000 }, { title: '1000', value: 1000 },
{ title: 'All', value: -1 }, { title: 'All', value: -1 },
] ]
+5 -12
View File
@@ -32,26 +32,22 @@ async function moduleTest(index: number) {
if (result.success) { if (result.success) {
target.state = 'success' target.state = 'success'
target.name = `${target.name} - 正常` target.name = `${target.name} - 正常`
} } else if (result.message?.includes('模块未加载')) {
else if (result.message?.includes('模块未加载')) {
target.state = '' target.state = ''
target.name = `${target.name} - 未启用` target.name = `${target.name} - 未启用`
} } else {
else {
target.state = 'error' target.state = 'error'
target.name = `${target.name} - 错误!` target.name = `${target.name} - 错误!`
target.errmsg = result.message target.errmsg = result.message
} }
} } catch (error) {
catch (error) {
console.error(error) console.error(error)
} }
} }
// 加载 // 加载
onMounted(async () => { onMounted(async () => {
// 逐个检查所有模块 // 逐个检查所有模块
for (let i = 0; i < modules.value.length; i++) for (let i = 0; i < modules.value.length; i++) await moduleTest(i)
await moduleTest(i)
}) })
</script> </script>
@@ -66,10 +62,7 @@ onMounted(async () => {
> >
{{ module.errmsg }} {{ module.errmsg }}
<template #append> <template #append>
<VProgressCircular <VProgressCircular v-if="module.loading" indeterminate />
v-if="module.loading"
indeterminate
/>
</template> </template>
</VAlert> </VAlert>
</template> </template>