mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-06 08:10:21 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09e42d5a08 | ||
|
|
b9a09fd1be | ||
|
|
b08235b9f6 | ||
|
|
43e67893b4 | ||
|
|
633b38da01 | ||
|
|
68a4818be0 | ||
|
|
be3e4a7b13 | ||
|
|
2bc616ebbb | ||
|
|
7058472784 | ||
|
|
8d9f28b3c8 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "moviepilot",
|
"name": "moviepilot",
|
||||||
"version": "1.1.3",
|
"version": "1.1.3-4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
rgba(#{variables.$vertical-nav-background-color-rgb}, 30%) 75%,
|
rgba(#{variables.$vertical-nav-background-color-rgb}, 30%) 75%,
|
||||||
transparent
|
transparent
|
||||||
);
|
);
|
||||||
block-size: calc(env(safe-area-inset-top) + 64px);
|
block-size: calc(env(safe-area-inset-top) + 4rem);
|
||||||
inline-size: 100%;
|
inline-size: 100%;
|
||||||
inset-block-start: calc(#{variables.$vertical-nav-header-height} - 2px);
|
inset-block-start: calc(#{variables.$vertical-nav-header-height} - 2px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
// 👉 Vertical nav
|
// 👉 Vertical nav
|
||||||
$layout-vertical-nav-z-index: 12 !default;
|
$layout-vertical-nav-z-index: 12 !default;
|
||||||
$layout-vertical-nav-width: 260px !default;
|
$layout-vertical-nav-width: 16.25rem !default;
|
||||||
$layout-vertical-nav-collapsed-width: 80px !default;
|
$layout-vertical-nav-collapsed-width: 80px !default;
|
||||||
|
|
||||||
// 👉 Horizontal nav
|
// 👉 Horizontal nav
|
||||||
$layout-horizontal-nav-z-index: 11 !default;
|
$layout-horizontal-nav-z-index: 11 !default;
|
||||||
$layout-horizontal-nav-navbar-height: 64px !default;
|
$layout-horizontal-nav-navbar-height: 4rem !default;
|
||||||
|
|
||||||
// 👉 Navbar
|
// 👉 Navbar
|
||||||
$layout-vertical-nav-navbar-height: 64px !default;
|
$layout-vertical-nav-navbar-height: 4rem !default;
|
||||||
$layout-vertical-nav-navbar-is-contained: true !default;
|
$layout-vertical-nav-navbar-is-contained: true !default;
|
||||||
$layout-vertical-nav-layout-navbar-z-index: 11 !default;
|
$layout-vertical-nav-layout-navbar-z-index: 11 !default;
|
||||||
$layout-horizontal-nav-layout-navbar-z-index: 11 !default;
|
$layout-horizontal-nav-layout-navbar-z-index: 11 !default;
|
||||||
@@ -19,7 +19,7 @@ $layout-horizontal-nav-layout-navbar-z-index: 11 !default;
|
|||||||
$layout-boxed-content-width: 1440px !default;
|
$layout-boxed-content-width: 1440px !default;
|
||||||
|
|
||||||
// 👉Footer
|
// 👉Footer
|
||||||
$layout-vertical-nav-footer-height: 56px !default;
|
$layout-vertical-nav-footer-height: 3.5rem !default;
|
||||||
|
|
||||||
// 👉 Layout overlay
|
// 👉 Layout overlay
|
||||||
$layout-overlay-z-index: 11 !default;
|
$layout-overlay-z-index: 11 !default;
|
||||||
|
|||||||
32
src/App.vue
32
src/App.vue
@@ -1,10 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useToast } from 'vue-toast-notification'
|
import { useToast } from 'vue-toast-notification'
|
||||||
import { useTheme } from 'vuetify'
|
import { useTheme } from 'vuetify'
|
||||||
import api from './api'
|
|
||||||
import type { User } from './api/types'
|
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import avatar1 from '@images/avatars/avatar-1.png'
|
|
||||||
|
|
||||||
// 第一时间应用主题
|
// 第一时间应用主题
|
||||||
const { global: globalTheme } = useTheme()
|
const { global: globalTheme } = useTheme()
|
||||||
@@ -36,39 +33,10 @@ function startSSEMessager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前用户信息
|
|
||||||
const accountInfo = ref<User>({
|
|
||||||
id: 0,
|
|
||||||
name: '',
|
|
||||||
password: '',
|
|
||||||
email: '',
|
|
||||||
is_active: false,
|
|
||||||
is_superuser: false,
|
|
||||||
avatar: avatar1,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 调用API,加载当前用户数据
|
|
||||||
async function loadAccountInfo() {
|
|
||||||
try {
|
|
||||||
const user: User = await api.get('user/current')
|
|
||||||
|
|
||||||
accountInfo.value = user
|
|
||||||
if (!accountInfo.value.avatar)
|
|
||||||
accountInfo.value.avatar = avatar1
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页面加载时,加载当前用户数据
|
// 页面加载时,加载当前用户数据
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
await loadAccountInfo()
|
|
||||||
startSSEMessager()
|
startSSEMessager()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 提供给所有元素复用
|
|
||||||
provide('accountInfo', accountInfo)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -462,7 +462,7 @@ const getImgUrl: Ref<string> = computed(() => {
|
|||||||
</VHover>
|
</VHover>
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="subscribeSeasonDialog"
|
v-model="subscribeSeasonDialog"
|
||||||
max-width="600"
|
max-width="50rem"
|
||||||
content-class="whitespace-nowrap"
|
content-class="whitespace-nowrap"
|
||||||
scrollable
|
scrollable
|
||||||
>
|
>
|
||||||
|
|||||||
147
src/components/cards/MediaInfoCard.vue
Normal file
147
src/components/cards/MediaInfoCard.vue
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import type { Context } from '@/api/types'
|
||||||
|
|
||||||
|
// 输入参数
|
||||||
|
const props = defineProps({
|
||||||
|
context: Object as PropType<Context>,
|
||||||
|
})
|
||||||
|
|
||||||
|
// TMDB图片转换为w500大小
|
||||||
|
function getW500Image(url = '') {
|
||||||
|
if (!url)
|
||||||
|
return ''
|
||||||
|
return url.replace('original', 'w500')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开TMDB详情页面
|
||||||
|
function openTmdbPage(type: string, tmdbId: number) {
|
||||||
|
if (!type || !tmdbId)
|
||||||
|
return
|
||||||
|
|
||||||
|
const url = `https://www.themoviedb.org/${type === '电影' ? 'movie' : 'tv'}/${tmdbId}`
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-show="context">
|
||||||
|
<VCol>
|
||||||
|
<div
|
||||||
|
v-if="context?.meta_info?.name"
|
||||||
|
class="d-flex justify-space-between flex-wrap flex-md-nowrap flex-column flex-md-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="context?.media_info?.poster_path"
|
||||||
|
class="ma-auto"
|
||||||
|
>
|
||||||
|
<VImg
|
||||||
|
width="10rem"
|
||||||
|
aspect-ratio="2/3"
|
||||||
|
class="object-cover aspect-w-2 aspect-h-3 rounded-lg ring-1 ring-gray-500"
|
||||||
|
:src="getW500Image(context?.media_info?.poster_path)"
|
||||||
|
cover
|
||||||
|
>
|
||||||
|
<template #placeholder>
|
||||||
|
<div class="w-full h-full">
|
||||||
|
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VImg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<VCardItem class="pb-1">
|
||||||
|
<VCardTitle>
|
||||||
|
{{ context?.media_info?.title || context?.meta_info?.name }}
|
||||||
|
{{ context?.meta_info?.season_episode }}
|
||||||
|
</VCardTitle>
|
||||||
|
<VCardSubtitle>
|
||||||
|
{{ context?.media_info?.year || context?.meta_info?.year }}
|
||||||
|
</VCardSubtitle>
|
||||||
|
</VCardItem>
|
||||||
|
|
||||||
|
<VCardText
|
||||||
|
v-if="context?.media_info?.overview"
|
||||||
|
class="line-clamp-4 overflow-hidden text-ellipsis ..."
|
||||||
|
>
|
||||||
|
{{ context?.media_info?.overview }}
|
||||||
|
</VCardText>
|
||||||
|
|
||||||
|
<VCardItem>
|
||||||
|
<!-- 类型 -->
|
||||||
|
<VChip
|
||||||
|
v-if="context?.media_info?.type || context?.meta_info?.type"
|
||||||
|
variant="elevated"
|
||||||
|
class="me-1 mb-1 text-white bg-blue-500"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
context?.media_info?.type || context?.meta_info?.type
|
||||||
|
}}
|
||||||
|
</VChip>
|
||||||
|
<!-- 二级分类 -->
|
||||||
|
<VChip
|
||||||
|
v-if="context?.media_info?.category"
|
||||||
|
variant="elevated"
|
||||||
|
class="me-1 mb-1 text-white bg-blue-500"
|
||||||
|
>
|
||||||
|
{{ context?.media_info?.category }}
|
||||||
|
</VChip>
|
||||||
|
<!-- TMDBID -->
|
||||||
|
<VChip
|
||||||
|
v-if="context?.media_info?.tmdb_id"
|
||||||
|
variant="elevated"
|
||||||
|
color="success"
|
||||||
|
class="me-1 mb-1"
|
||||||
|
@click="openTmdbPage(context?.media_info?.type || '', context?.media_info?.tmdb_id)"
|
||||||
|
>
|
||||||
|
{{ context?.media_info?.tmdb_id }}
|
||||||
|
</VChip>
|
||||||
|
<!-- meta_info -->
|
||||||
|
<VChip
|
||||||
|
v-if="context?.meta_info?.edition"
|
||||||
|
variant="elevated"
|
||||||
|
class="me-1 mb-1 text-white bg-red-500"
|
||||||
|
>
|
||||||
|
{{ context?.meta_info?.edition }}
|
||||||
|
</VChip>
|
||||||
|
<VChip
|
||||||
|
v-if="context?.meta_info?.resource_pix"
|
||||||
|
variant="elevated"
|
||||||
|
class="me-1 mb-1 text-white bg-red-500"
|
||||||
|
>
|
||||||
|
{{ context?.meta_info?.resource_pix }}
|
||||||
|
</VChip>
|
||||||
|
<VChip
|
||||||
|
v-if="context?.meta_info?.video_encode"
|
||||||
|
variant="elevated"
|
||||||
|
class="me-1 mb-1 text-white bg-orange-500"
|
||||||
|
>
|
||||||
|
{{ context?.meta_info?.video_encode }}
|
||||||
|
</VChip>
|
||||||
|
<VChip
|
||||||
|
v-if="context?.meta_info?.audio_encode"
|
||||||
|
variant="elevated"
|
||||||
|
class="me-1 mb-1 text-white bg-orange-500"
|
||||||
|
>
|
||||||
|
{{ context?.meta_info?.audio_encode }}
|
||||||
|
</VChip>
|
||||||
|
<VChip
|
||||||
|
v-if="context?.meta_info?.resource_team"
|
||||||
|
variant="elevated"
|
||||||
|
class="me-1 mb-1 text-white bg-cyan-500"
|
||||||
|
>
|
||||||
|
{{ context?.meta_info?.resource_team }}
|
||||||
|
</VChip>
|
||||||
|
</VCardItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<VAlert
|
||||||
|
v-if="!context?.meta_info?.name"
|
||||||
|
icon="mdi-alert-circle-outline"
|
||||||
|
>
|
||||||
|
识别失败,无法识别到有效信息!
|
||||||
|
</VAlert>
|
||||||
|
</VCol>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -389,7 +389,7 @@ onMounted(() => {
|
|||||||
<!-- 更新站点Cookie & UA弹窗 -->
|
<!-- 更新站点Cookie & UA弹窗 -->
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="siteCookieDialog"
|
v-model="siteCookieDialog"
|
||||||
max-width="600"
|
max-width="50rem"
|
||||||
>
|
>
|
||||||
<!-- Dialog Content -->
|
<!-- Dialog Content -->
|
||||||
<VCard title="更新站点Cookie & UA">
|
<VCard title="更新站点Cookie & UA">
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ async function handleAddDownload(_site: any = undefined,
|
|||||||
confirmationText: '确认',
|
confirmationText: '确认',
|
||||||
cancellationText: '取消',
|
cancellationText: '取消',
|
||||||
dialogProps: {
|
dialogProps: {
|
||||||
maxWidth: 600,
|
maxWidth: '50rem',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { formatBytes } from '@core/utils/formatters'
|
|||||||
import type { Context, EndPoints, FileItem } from '@/api/types'
|
import type { Context, EndPoints, FileItem } from '@/api/types'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
import MediaInfoCard from '@/components/cards/MediaInfoCard.vue'
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const inProps = defineProps({
|
const inProps = defineProps({
|
||||||
@@ -87,6 +88,9 @@ const transferForm = reactive({
|
|||||||
// 识别结果
|
// 识别结果
|
||||||
const nameTestResult = ref<Context>()
|
const nameTestResult = ref<Context>()
|
||||||
|
|
||||||
|
// 识别结果对话框
|
||||||
|
const nameTestDialog = ref(false)
|
||||||
|
|
||||||
// 生成1到50季的下拉框选项
|
// 生成1到50季的下拉框选项
|
||||||
const seasonItems = ref(
|
const seasonItems = ref(
|
||||||
Array.from({ length: 51 }, (_, i) => i).map(item => ({
|
Array.from({ length: 51 }, (_, i) => i).map(item => ({
|
||||||
@@ -147,7 +151,7 @@ async function deleteItem(item: FileItem) {
|
|||||||
confirmationText: '确认',
|
confirmationText: '确认',
|
||||||
cancellationText: '取消',
|
cancellationText: '取消',
|
||||||
dialogProps: {
|
dialogProps: {
|
||||||
maxWidth: 600,
|
maxWidth: '50rem',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -277,6 +281,7 @@ watch(
|
|||||||
async () => {
|
async () => {
|
||||||
items.value = []
|
items.value = []
|
||||||
nameTestResult.value = undefined
|
nameTestResult.value = undefined
|
||||||
|
nameTestDialog.value = false
|
||||||
await load()
|
await load()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -321,6 +326,7 @@ async function recognize(path: string) {
|
|||||||
// 显示进度条
|
// 显示进度条
|
||||||
progressDialog.value = true
|
progressDialog.value = true
|
||||||
progressText.value = `正在识别 ${path} ...`
|
progressText.value = `正在识别 ${path} ...`
|
||||||
|
progressValue.value = 0
|
||||||
nameTestResult.value = await api.get('media/recognize_file', {
|
nameTestResult.value = await api.get('media/recognize_file', {
|
||||||
params: {
|
params: {
|
||||||
path,
|
path,
|
||||||
@@ -330,24 +336,27 @@ async function recognize(path: string) {
|
|||||||
progressDialog.value = false
|
progressDialog.value = false
|
||||||
if (!nameTestResult.value)
|
if (!nameTestResult.value)
|
||||||
$toast.error(`${path} 识别失败!`)
|
$toast.error(`${path} 识别失败!`)
|
||||||
|
nameTestDialog.value = !!nameTestResult.value?.meta_info?.name
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TMDB图片转换为w500大小
|
|
||||||
function getW500Image(url = '') {
|
|
||||||
if (!url)
|
|
||||||
return ''
|
|
||||||
return url.replace('original', 'w500')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 弹出菜单
|
// 弹出菜单
|
||||||
const dropdownItems = ref([
|
const dropdownItems = ref([
|
||||||
{
|
{
|
||||||
title: '重命名',
|
title: '识别',
|
||||||
value: 1,
|
value: 1,
|
||||||
|
props: {
|
||||||
|
prependIcon: 'mdi-text-recognition',
|
||||||
|
click: (_item: FileItem) => {
|
||||||
|
recognize(_item.path || '')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
title: '重命名',
|
||||||
|
value: 2,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-rename',
|
prependIcon: 'mdi-rename',
|
||||||
click: showRenmae,
|
click: showRenmae,
|
||||||
@@ -355,7 +364,7 @@ const dropdownItems = ref([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '整理',
|
title: '整理',
|
||||||
value: 2,
|
value: 3,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-folder-arrow-right',
|
prependIcon: 'mdi-folder-arrow-right',
|
||||||
click: showTransfer,
|
click: showTransfer,
|
||||||
@@ -363,7 +372,7 @@ const dropdownItems = ref([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '删除',
|
title: '删除',
|
||||||
value: 3,
|
value: 4,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-delete-outline',
|
prependIcon: 'mdi-delete-outline',
|
||||||
color: 'error',
|
color: 'error',
|
||||||
@@ -400,114 +409,6 @@ onMounted(() => {
|
|||||||
class="text-center break-all"
|
class="text-center break-all"
|
||||||
>
|
>
|
||||||
文件: {{ path }}
|
文件: {{ path }}
|
||||||
<VDivider v-if="nameTestResult" class="my-3" />
|
|
||||||
<div
|
|
||||||
v-if="nameTestResult?.meta_info?.name"
|
|
||||||
class="d-flex justify-space-between flex-wrap flex-md-nowrap flex-column flex-md-row"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="nameTestResult?.media_info?.poster_path"
|
|
||||||
>
|
|
||||||
<VImg
|
|
||||||
width="10rem"
|
|
||||||
aspect-ratio="2/3"
|
|
||||||
class="object-cover aspect-w-2 aspect-h-3 rounded-lg ring-1 ring-gray-500"
|
|
||||||
:src="getW500Image(nameTestResult?.media_info?.poster_path)"
|
|
||||||
cover
|
|
||||||
>
|
|
||||||
<template #placeholder>
|
|
||||||
<div class="w-full h-full">
|
|
||||||
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</VImg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-start grow">
|
|
||||||
<VCardItem class="pb-1">
|
|
||||||
<VCardTitle>
|
|
||||||
{{ nameTestResult?.media_info?.title || nameTestResult?.meta_info?.name }}
|
|
||||||
{{ nameTestResult?.meta_info?.season_episode }}
|
|
||||||
</VCardTitle>
|
|
||||||
<VCardSubtitle>
|
|
||||||
{{ nameTestResult?.media_info?.year || nameTestResult?.meta_info?.year }}
|
|
||||||
</VCardSubtitle>
|
|
||||||
</VCardItem>
|
|
||||||
|
|
||||||
<VCardText
|
|
||||||
v-if="nameTestResult?.media_info?.overview"
|
|
||||||
class="line-clamp-4 overflow-hidden text-ellipsis ..."
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.media_info?.overview }}
|
|
||||||
</VCardText>
|
|
||||||
|
|
||||||
<VCardItem>
|
|
||||||
<!-- 类型 -->
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.media_info?.type || nameTestResult?.meta_info?.type"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-blue-500"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
nameTestResult?.media_info?.type || nameTestResult?.meta_info?.type
|
|
||||||
}}
|
|
||||||
</VChip>
|
|
||||||
<!-- 二级分类 -->
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.media_info?.category"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-blue-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.media_info?.category }}
|
|
||||||
</VChip>
|
|
||||||
<!-- TMDBID -->
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.media_info?.tmdb_id"
|
|
||||||
variant="elevated"
|
|
||||||
color="success"
|
|
||||||
class="me-1 mb-1"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.media_info?.tmdb_id }}
|
|
||||||
</VChip>
|
|
||||||
<!-- meta_info -->
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.edition"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-red-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.edition }}
|
|
||||||
</VChip>
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.resource_pix"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-red-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.resource_pix }}
|
|
||||||
</VChip>
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.video_encode"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-orange-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.video_encode }}
|
|
||||||
</VChip>
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.audio_encode"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-orange-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.audio_encode }}
|
|
||||||
</VChip>
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.resource_team"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-cyan-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.resource_team }}
|
|
||||||
</VChip>
|
|
||||||
</VCardItem>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardText
|
<VCardText
|
||||||
v-else-if="isFile && isImage"
|
v-else-if="isFile && isImage"
|
||||||
@@ -553,6 +454,9 @@ onMounted(() => {
|
|||||||
</VList>
|
</VList>
|
||||||
</VMenu>
|
</VMenu>
|
||||||
</IconBtn>
|
</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)">
|
<IconBtn class="d-none d-sm-block" @click.stop="showRenmae(item)">
|
||||||
<VIcon icon="mdi-rename" />
|
<VIcon icon="mdi-rename" />
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
@@ -606,6 +510,9 @@ onMounted(() => {
|
|||||||
</VList>
|
</VList>
|
||||||
</VMenu>
|
</VMenu>
|
||||||
</IconBtn>
|
</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)">
|
<IconBtn class="d-none d-sm-block" @click.stop="showRenmae(item)">
|
||||||
<VIcon icon="mdi-rename" />
|
<VIcon icon="mdi-rename" />
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
@@ -665,7 +572,7 @@ onMounted(() => {
|
|||||||
<!-- 重命名弹窗 -->
|
<!-- 重命名弹窗 -->
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="renamePopper"
|
v-model="renamePopper"
|
||||||
max-width="600"
|
max-width="50rem"
|
||||||
>
|
>
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props }">
|
||||||
<IconBtn title="重命名" v-bind="props">
|
<IconBtn title="重命名" v-bind="props">
|
||||||
@@ -831,6 +738,7 @@ onMounted(() => {
|
|||||||
<vCardText class="text-center">
|
<vCardText class="text-center">
|
||||||
{{ progressText }}
|
{{ progressText }}
|
||||||
<vProgressLinear
|
<vProgressLinear
|
||||||
|
v-if="progressValue"
|
||||||
color="white"
|
color="white"
|
||||||
class="mb-0 mt-1"
|
class="mb-0 mt-1"
|
||||||
:model-value="progressValue"
|
:model-value="progressValue"
|
||||||
@@ -838,6 +746,19 @@ onMounted(() => {
|
|||||||
</vCardText>
|
</vCardText>
|
||||||
</vCard>
|
</vCard>
|
||||||
</vDialog>
|
</vDialog>
|
||||||
|
<!-- 识别结果对话框 -->
|
||||||
|
<vDialog
|
||||||
|
v-model="nameTestDialog"
|
||||||
|
:scrim="false"
|
||||||
|
width="800"
|
||||||
|
>
|
||||||
|
<vCard>
|
||||||
|
<DialogCloseBtn @click="nameTestDialog = false" />
|
||||||
|
<VCardItem>
|
||||||
|
<MediaInfoCard :context="nameTestResult" />
|
||||||
|
</VCardItem>
|
||||||
|
</vCard>
|
||||||
|
</vDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ async function mkdir() {
|
|||||||
</IconBtn>
|
</IconBtn>
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="newFolderPopper"
|
v-model="newFolderPopper"
|
||||||
max-width="600"
|
max-width="50rem"
|
||||||
>
|
>
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props }">
|
||||||
<IconBtn title="新建文件夹" v-bind="props">
|
<IconBtn title="新建文件夹" v-bind="props">
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue'
|
|||||||
import SearchBar from '@/layouts/components/SearchBar.vue'
|
import SearchBar from '@/layouts/components/SearchBar.vue'
|
||||||
import ShortcutBar from '@/layouts/components/ShortcutBar.vue'
|
import ShortcutBar from '@/layouts/components/ShortcutBar.vue'
|
||||||
import UserProfile from '@/layouts/components/UserProfile.vue'
|
import UserProfile from '@/layouts/components/UserProfile.vue'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
// 从Vuex Store中获取superuser信息
|
||||||
|
const superUser = store.state.auth.superUser
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -87,6 +91,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<VerticalNavLink
|
<VerticalNavLink
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
title: '电影',
|
title: '电影',
|
||||||
icon: 'mdi-movie-check-outline',
|
icon: 'mdi-movie-check-outline',
|
||||||
@@ -94,6 +99,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<VerticalNavLink
|
<VerticalNavLink
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
title: '电视剧',
|
title: '电视剧',
|
||||||
icon: 'mdi-television-classic',
|
icon: 'mdi-television-classic',
|
||||||
@@ -101,6 +107,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<VerticalNavLink
|
<VerticalNavLink
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
title: '自定义',
|
title: '自定义',
|
||||||
icon: 'mdi-rss',
|
icon: 'mdi-rss',
|
||||||
@@ -128,6 +135,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<VerticalNavLink
|
<VerticalNavLink
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
title: '历史记录',
|
title: '历史记录',
|
||||||
icon: 'mdi-history',
|
icon: 'mdi-history',
|
||||||
@@ -135,6 +143,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<VerticalNavLink
|
<VerticalNavLink
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
title: '文件管理',
|
title: '文件管理',
|
||||||
icon: 'mdi-folder-multiple-outline',
|
icon: 'mdi-folder-multiple-outline',
|
||||||
@@ -144,11 +153,13 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
|
|||||||
|
|
||||||
<!-- 👉 系统 -->
|
<!-- 👉 系统 -->
|
||||||
<VerticalNavSectionTitle
|
<VerticalNavSectionTitle
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
heading: '系统',
|
heading: '系统',
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<VerticalNavLink
|
<VerticalNavLink
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
title: '插件',
|
title: '插件',
|
||||||
icon: 'mdi-apps',
|
icon: 'mdi-apps',
|
||||||
@@ -156,6 +167,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<VerticalNavLink
|
<VerticalNavLink
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
title: '站点管理',
|
title: '站点管理',
|
||||||
icon: 'mdi-web',
|
icon: 'mdi-web',
|
||||||
@@ -163,6 +175,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<VerticalNavLink
|
<VerticalNavLink
|
||||||
|
v-if="superUser"
|
||||||
:item="{
|
:item="{
|
||||||
title: '设定',
|
title: '设定',
|
||||||
icon: 'mdi-cog',
|
icon: 'mdi-cog',
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ function search() {
|
|||||||
>
|
>
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="searchDialog"
|
v-model="searchDialog"
|
||||||
max-width="600"
|
max-width="50rem"
|
||||||
transition="dialog-top-transition"
|
transition="dialog-top-transition"
|
||||||
>
|
>
|
||||||
<!-- Dialog Activator -->
|
<!-- Dialog Activator -->
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ const ruleTestDialog = ref(false)
|
|||||||
<!-- 名称测试弹窗 -->
|
<!-- 名称测试弹窗 -->
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="nameTestDialog"
|
v-model="nameTestDialog"
|
||||||
max-width="800"
|
max-width="50rem"
|
||||||
>
|
>
|
||||||
<VCard title="名称识别测试">
|
<VCard title="名称识别测试">
|
||||||
<DialogCloseBtn @click="nameTestDialog = false" />
|
<DialogCloseBtn @click="nameTestDialog = false" />
|
||||||
@@ -156,7 +156,7 @@ const ruleTestDialog = ref(false)
|
|||||||
<!-- 网络测试弹窗 -->
|
<!-- 网络测试弹窗 -->
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="netTestDialog"
|
v-model="netTestDialog"
|
||||||
max-width="600"
|
max-width="35rem"
|
||||||
>
|
>
|
||||||
<VCard title="网络测试">
|
<VCard title="网络测试">
|
||||||
<DialogCloseBtn @click="netTestDialog = false" />
|
<DialogCloseBtn @click="netTestDialog = false" />
|
||||||
@@ -168,7 +168,7 @@ const ruleTestDialog = ref(false)
|
|||||||
<!-- 实时日志弹窗 -->
|
<!-- 实时日志弹窗 -->
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="loggingDialog"
|
v-model="loggingDialog"
|
||||||
max-width="1280"
|
class="w-full lg:w-4/5"
|
||||||
scrollable
|
scrollable
|
||||||
>
|
>
|
||||||
<VCard title="实时日志">
|
<VCard title="实时日志">
|
||||||
@@ -181,7 +181,7 @@ const ruleTestDialog = ref(false)
|
|||||||
<!-- 规则测试弹窗 -->
|
<!-- 规则测试弹窗 -->
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="ruleTestDialog"
|
v-model="ruleTestDialog"
|
||||||
max-width="800"
|
max-width="50rem"
|
||||||
scrollable
|
scrollable
|
||||||
>
|
>
|
||||||
<VCard title="过滤规则测试">
|
<VCard title="过滤规则测试">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
import avatar1 from '@images/avatars/avatar-1.png'
|
||||||
|
|
||||||
// Vuex Store
|
// Vuex Store
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
@@ -14,8 +15,10 @@ function logout() {
|
|||||||
router.push('/login')
|
router.push('/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户信息
|
// 从Vuex Store中获取信息
|
||||||
const accountInfo: any = inject('accountInfo')
|
const superUser = store.state.auth.superUser
|
||||||
|
const userName = store.state.auth.userName
|
||||||
|
const avatar = store.state.auth.avatar
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -24,7 +27,7 @@ const accountInfo: any = inject('accountInfo')
|
|||||||
color="primary"
|
color="primary"
|
||||||
variant="tonal"
|
variant="tonal"
|
||||||
>
|
>
|
||||||
<VImg :src="accountInfo.avatar" />
|
<VImg :src="avatar ?? avatar1" />
|
||||||
|
|
||||||
<!-- SECTION Menu -->
|
<!-- SECTION Menu -->
|
||||||
<VMenu
|
<VMenu
|
||||||
@@ -42,20 +45,21 @@ const accountInfo: any = inject('accountInfo')
|
|||||||
color="primary"
|
color="primary"
|
||||||
variant="tonal"
|
variant="tonal"
|
||||||
>
|
>
|
||||||
<VImg :src="accountInfo.avatar" />
|
<VImg :src="avatar ?? avatar1" />
|
||||||
</VAvatar>
|
</VAvatar>
|
||||||
</VListItemAction>
|
</VListItemAction>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<VListItemTitle class="font-weight-semibold">
|
<VListItemTitle class="font-weight-semibold">
|
||||||
{{ accountInfo.is_superuser ? "管理员" : "普通用户" }}
|
{{ superUser ? "管理员" : "普通用户" }}
|
||||||
</VListItemTitle>
|
</VListItemTitle>
|
||||||
<VListItemSubtitle>{{ accountInfo.name }}</VListItemSubtitle>
|
<VListItemSubtitle>{{ userName }}</VListItemSubtitle>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
<VDivider class="my-2" />
|
<VDivider class="my-2" />
|
||||||
|
|
||||||
<!-- 👉 Profile -->
|
<!-- 👉 Profile -->
|
||||||
<VListItem
|
<VListItem
|
||||||
|
v-if="superUser"
|
||||||
link
|
link
|
||||||
to="setting"
|
to="setting"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AnalyticsMediaStatistic from '@/views/dashboard/AnalyticsMediaStatistic.vue'
|
import AnalyticsMediaStatistic from '@/views/dashboard/AnalyticsMediaStatistic.vue'
|
||||||
import AnalyticsProcesses from '@/views/dashboard/AnalyticsProcesses.vue'
|
|
||||||
import AnalyticsScheduler from '@/views/dashboard/AnalyticsScheduler.vue'
|
import AnalyticsScheduler from '@/views/dashboard/AnalyticsScheduler.vue'
|
||||||
import AnalyticsSpeed from '@/views/dashboard/AnalyticsSpeed.vue'
|
import AnalyticsSpeed from '@/views/dashboard/AnalyticsSpeed.vue'
|
||||||
import AnalyticsStorage from '@/views/dashboard/AnalyticsStorage.vue'
|
import AnalyticsStorage from '@/views/dashboard/AnalyticsStorage.vue'
|
||||||
import AnalyticsWeeklyOverview from '@/views/dashboard/AnalyticsWeeklyOverview.vue'
|
import AnalyticsWeeklyOverview from '@/views/dashboard/AnalyticsWeeklyOverview.vue'
|
||||||
|
import AnalyticsCpu from '@/views/dashboard/AnalyticsCpu.vue'
|
||||||
|
import AnalyticsMemory from '@/views/dashboard/AnalyticsMemory.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -44,8 +45,18 @@ import AnalyticsWeeklyOverview from '@/views/dashboard/AnalyticsWeeklyOverview.v
|
|||||||
<AnalyticsScheduler />
|
<AnalyticsScheduler />
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
<VCol cols="12">
|
<VCol
|
||||||
<AnalyticsProcesses />
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<AnalyticsCpu />
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<AnalyticsMemory />
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -66,10 +66,16 @@ function login() {
|
|||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
// 获取token
|
// 获取token
|
||||||
const token = response.access_token
|
const token = response.access_token
|
||||||
|
const superuser = response.super_user
|
||||||
|
const username = response.user_name
|
||||||
|
const avatar = response.avatar
|
||||||
|
|
||||||
// 更新token和remember状态到Vuex Store
|
// 更新token和remember状态到Vuex Store
|
||||||
store.dispatch('auth/updateToken', token)
|
store.dispatch('auth/updateToken', token)
|
||||||
store.dispatch('auth/updateRemember', form.value.remember)
|
store.dispatch('auth/updateRemember', form.value.remember)
|
||||||
|
store.dispatch('auth/updateSuperUser', superuser)
|
||||||
|
store.dispatch('auth/updateUserName', username)
|
||||||
|
store.dispatch('auth/updateAvatar', avatar)
|
||||||
|
|
||||||
// 跳转到首页
|
// 跳转到首页
|
||||||
router.push('/')
|
router.push('/')
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import type { Module } from 'vuex'
|
|||||||
interface AuthState {
|
interface AuthState {
|
||||||
token: string | null
|
token: string | null
|
||||||
remember: boolean
|
remember: boolean
|
||||||
|
superUser: boolean
|
||||||
|
userName: string
|
||||||
|
avatar: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义根状态类型
|
// 定义根状态类型
|
||||||
@@ -17,6 +20,9 @@ const authModule: Module<AuthState, RootState> = {
|
|||||||
state: {
|
state: {
|
||||||
token: null,
|
token: null,
|
||||||
remember: false,
|
remember: false,
|
||||||
|
superUser: false,
|
||||||
|
userName: '',
|
||||||
|
avatar: '',
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setToken(state, token: string) {
|
setToken(state, token: string) {
|
||||||
@@ -28,6 +34,15 @@ const authModule: Module<AuthState, RootState> = {
|
|||||||
setRemember(state, remember: boolean) {
|
setRemember(state, remember: boolean) {
|
||||||
state.remember = remember
|
state.remember = remember
|
||||||
},
|
},
|
||||||
|
setSuperUser(state, superUser: boolean) {
|
||||||
|
state.superUser = superUser
|
||||||
|
},
|
||||||
|
setUserName(state, userName: string) {
|
||||||
|
state.userName = userName
|
||||||
|
},
|
||||||
|
setAvatar(state, avatar: string) {
|
||||||
|
state.avatar = avatar
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
updateToken({ commit }, token: string) {
|
updateToken({ commit }, token: string) {
|
||||||
@@ -39,10 +54,22 @@ const authModule: Module<AuthState, RootState> = {
|
|||||||
updateRemember({ commit }, remember: boolean) {
|
updateRemember({ commit }, remember: boolean) {
|
||||||
commit('setRemember', remember)
|
commit('setRemember', remember)
|
||||||
},
|
},
|
||||||
|
updateSuperUser({ commit }, superUser: boolean) {
|
||||||
|
commit('setSuperUser', superUser)
|
||||||
|
},
|
||||||
|
updateUserName({ commit }, userName: string) {
|
||||||
|
commit('setUserName', userName)
|
||||||
|
},
|
||||||
|
updateAvatar({ commit }, avatar: string) {
|
||||||
|
commit('setAvatar', avatar)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getToken: state => state.token,
|
getToken: state => state.token,
|
||||||
getRemember: state => state.remember,
|
getRemember: state => state.remember,
|
||||||
|
getSuperUser: state => state.superUser,
|
||||||
|
getUserName: state => state.userName,
|
||||||
|
getAvatar: state => state.avatar,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
134
src/views/dashboard/AnalyticsCpu.vue
Normal file
134
src/views/dashboard/AnalyticsCpu.vue
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import VueApexCharts from 'vue3-apexcharts'
|
||||||
|
import { useTheme } from 'vuetify'
|
||||||
|
import { hexToRgb } from '@layouts/utils'
|
||||||
|
import api from '@/api'
|
||||||
|
|
||||||
|
const vuetifyTheme = useTheme()
|
||||||
|
|
||||||
|
const currentTheme = controlledComputed(() => vuetifyTheme.name.value, () => vuetifyTheme.current.value.colors)
|
||||||
|
const variableTheme = controlledComputed(() => vuetifyTheme.name.value, () => vuetifyTheme.current.value.variables)
|
||||||
|
|
||||||
|
// 定时器
|
||||||
|
let refreshTimer: NodeJS.Timer | null = null
|
||||||
|
|
||||||
|
// 时间序列
|
||||||
|
const series = ref([
|
||||||
|
{
|
||||||
|
data: [0],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 当前值
|
||||||
|
const current = ref(0)
|
||||||
|
|
||||||
|
const chartOptions = controlledComputed(() => vuetifyTheme.name.value, () => {
|
||||||
|
return {
|
||||||
|
chart: {
|
||||||
|
parentHeightOffset: 0,
|
||||||
|
toolbar: { show: false },
|
||||||
|
animations: { enabled: false },
|
||||||
|
},
|
||||||
|
tooltip: { enabled: false },
|
||||||
|
grid: {
|
||||||
|
borderColor: `rgba(${hexToRgb(String(variableTheme.value['border-color']))},${variableTheme.value['border-opacity']})`,
|
||||||
|
strokeDashArray: 6,
|
||||||
|
xaxis: {
|
||||||
|
lines: { show: false },
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
lines: { show: true },
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
top: -10,
|
||||||
|
left: -7,
|
||||||
|
right: 5,
|
||||||
|
bottom: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
width: 3,
|
||||||
|
lineCap: 'butt',
|
||||||
|
curve: 'smooth',
|
||||||
|
},
|
||||||
|
colors: [currentTheme.value.primary],
|
||||||
|
markers: {
|
||||||
|
size: 6,
|
||||||
|
offsetY: 4,
|
||||||
|
offsetX: -2,
|
||||||
|
strokeWidth: 3,
|
||||||
|
colors: ['transparent'],
|
||||||
|
strokeColors: 'transparent',
|
||||||
|
discrete: [
|
||||||
|
{
|
||||||
|
size: 5.5,
|
||||||
|
seriesIndex: 0,
|
||||||
|
strokeColor: currentTheme.value.primary,
|
||||||
|
fillColor: currentTheme.value.surface,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hover: { size: 7 },
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
labels: { show: false },
|
||||||
|
axisTicks: { show: false },
|
||||||
|
axisBorder: { show: false },
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: { show: false },
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用API接口获取最新CPU使用率
|
||||||
|
async function getCpuUsage() {
|
||||||
|
try {
|
||||||
|
// 请求数据
|
||||||
|
current.value = await api.get('dashboard/cpu') ?? 0
|
||||||
|
// 添加到序列
|
||||||
|
series.value[0].data.push(current.value)
|
||||||
|
// 序列超过30条记录时,清掉前面的
|
||||||
|
if (series.value[0].data.length > 30)
|
||||||
|
series.value[0].data.shift()
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCpuUsage()// 启动定时器
|
||||||
|
refreshTimer = setInterval(() => {
|
||||||
|
getCpuUsage()
|
||||||
|
}, 2000)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件卸载时停止定时器
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (refreshTimer) {
|
||||||
|
clearInterval(refreshTimer)
|
||||||
|
refreshTimer = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VCard>
|
||||||
|
<VCardText>
|
||||||
|
<h6 class="text-h6">
|
||||||
|
CPU
|
||||||
|
</h6>
|
||||||
|
<VueApexCharts
|
||||||
|
type="line"
|
||||||
|
:options="chartOptions"
|
||||||
|
:series="series"
|
||||||
|
:height="150"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="text-center font-weight-medium mb-0">
|
||||||
|
当前:{{ current }}%
|
||||||
|
</p>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</template>
|
||||||
138
src/views/dashboard/AnalyticsMemory.vue
Normal file
138
src/views/dashboard/AnalyticsMemory.vue
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import VueApexCharts from 'vue3-apexcharts'
|
||||||
|
import { useTheme } from 'vuetify'
|
||||||
|
import { hexToRgb } from '@layouts/utils'
|
||||||
|
import api from '@/api'
|
||||||
|
import { formatBytes } from '@/@core/utils/formatters'
|
||||||
|
|
||||||
|
const vuetifyTheme = useTheme()
|
||||||
|
|
||||||
|
const currentTheme = controlledComputed(() => vuetifyTheme.name.value, () => vuetifyTheme.current.value.colors)
|
||||||
|
const variableTheme = controlledComputed(() => vuetifyTheme.name.value, () => vuetifyTheme.current.value.variables)
|
||||||
|
|
||||||
|
// 定时器
|
||||||
|
let refreshTimer: NodeJS.Timer | null = null
|
||||||
|
|
||||||
|
// 时间序列
|
||||||
|
const series = ref([
|
||||||
|
{
|
||||||
|
data: [0],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 当前值
|
||||||
|
const current = ref(0)
|
||||||
|
|
||||||
|
const chartOptions = controlledComputed(() => vuetifyTheme.name.value, () => {
|
||||||
|
return {
|
||||||
|
chart: {
|
||||||
|
parentHeightOffset: 0,
|
||||||
|
toolbar: { show: false },
|
||||||
|
animations: { enabled: false },
|
||||||
|
},
|
||||||
|
tooltip: { enabled: false },
|
||||||
|
grid: {
|
||||||
|
borderColor: `rgba(${hexToRgb(String(variableTheme.value['border-color']))},${variableTheme.value['border-opacity']})`,
|
||||||
|
strokeDashArray: 6,
|
||||||
|
xaxis: {
|
||||||
|
lines: { show: false },
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
lines: { show: true },
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
top: -10,
|
||||||
|
left: -7,
|
||||||
|
right: 5,
|
||||||
|
bottom: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
width: 3,
|
||||||
|
lineCap: 'butt',
|
||||||
|
curve: 'smooth',
|
||||||
|
},
|
||||||
|
colors: [currentTheme.value.primary],
|
||||||
|
markers: {
|
||||||
|
size: 6,
|
||||||
|
offsetY: 4,
|
||||||
|
offsetX: -2,
|
||||||
|
strokeWidth: 3,
|
||||||
|
colors: ['transparent'],
|
||||||
|
strokeColors: 'transparent',
|
||||||
|
discrete: [
|
||||||
|
{
|
||||||
|
size: 5.5,
|
||||||
|
seriesIndex: 0,
|
||||||
|
strokeColor: currentTheme.value.primary,
|
||||||
|
fillColor: currentTheme.value.surface,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hover: { size: 7 },
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
labels: { show: false },
|
||||||
|
axisTicks: { show: false },
|
||||||
|
axisBorder: { show: false },
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: { show: false },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用API接口获取最新内存使用量
|
||||||
|
async function getMemorgUsage() {
|
||||||
|
try {
|
||||||
|
// 请求数据
|
||||||
|
current.value = await api.get('dashboard/memory') ?? 0
|
||||||
|
// 添加到序列
|
||||||
|
series.value[0].data.push(current.value / 1024 / 1024 / 1024)
|
||||||
|
// 序列超过30条记录时,清掉前面的
|
||||||
|
if (series.value[0].data.length > 30)
|
||||||
|
series.value[0].data.shift()
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getMemorgUsage()
|
||||||
|
// 启动定时器
|
||||||
|
refreshTimer = setInterval(() => {
|
||||||
|
getMemorgUsage()
|
||||||
|
}, 3000)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件卸载时停止定时器
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (refreshTimer) {
|
||||||
|
clearInterval(refreshTimer)
|
||||||
|
refreshTimer = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VCard>
|
||||||
|
<VCardText>
|
||||||
|
<h6 class="text-h6">
|
||||||
|
内存
|
||||||
|
</h6>
|
||||||
|
<VueApexCharts
|
||||||
|
type="area"
|
||||||
|
:options="chartOptions"
|
||||||
|
:series="series"
|
||||||
|
:height="150"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="text-center font-weight-medium mb-0">
|
||||||
|
当前:{{ formatBytes(current) }}
|
||||||
|
</p>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</template>
|
||||||
@@ -131,7 +131,7 @@ async function removeHistory(item: TransferHistory) {
|
|||||||
confirmationText: '同步删除文件',
|
confirmationText: '同步删除文件',
|
||||||
cancellationText: '仅删除历史记录',
|
cancellationText: '仅删除历史记录',
|
||||||
dialogProps: {
|
dialogProps: {
|
||||||
maxWidth: 600,
|
maxWidth: '50rem',
|
||||||
},
|
},
|
||||||
confirmationButtonProps: {
|
confirmationButtonProps: {
|
||||||
color: 'error',
|
color: 'error',
|
||||||
@@ -180,7 +180,7 @@ async function removeHistoryBatch() {
|
|||||||
confirmationText: '同步删除文件',
|
confirmationText: '同步删除文件',
|
||||||
cancellationText: '仅删除历史记录',
|
cancellationText: '仅删除历史记录',
|
||||||
dialogProps: {
|
dialogProps: {
|
||||||
maxWidth: 600,
|
maxWidth: '50rem',
|
||||||
},
|
},
|
||||||
confirmationButtonProps: {
|
confirmationButtonProps: {
|
||||||
color: 'error',
|
color: 'error',
|
||||||
@@ -393,7 +393,7 @@ const dropdownItems = ref([
|
|||||||
</VCard>
|
</VCard>
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="redoDialog"
|
v-model="redoDialog"
|
||||||
max-width="600"
|
max-width="50rem"
|
||||||
>
|
>
|
||||||
<!-- Dialog Content -->
|
<!-- Dialog Content -->
|
||||||
<VCard title="重新整理">
|
<VCard title="重新整理">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { reactive, ref } from 'vue'
|
|||||||
import { requiredValidator } from '@/@validators'
|
import { requiredValidator } from '@/@validators'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import type { Context } from '@/api/types'
|
import type { Context } from '@/api/types'
|
||||||
|
import MediaInfoCard from '@/components/cards/MediaInfoCard.vue'
|
||||||
|
|
||||||
// 识别结果
|
// 识别结果
|
||||||
const nameTestResult = ref<Context>()
|
const nameTestResult = ref<Context>()
|
||||||
@@ -45,22 +46,6 @@ async function nameTest() {
|
|||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开TMDB详情页面
|
|
||||||
function openTmdbPage(type: string, tmdbId: number) {
|
|
||||||
if (!type || !tmdbId)
|
|
||||||
return
|
|
||||||
|
|
||||||
const url = `https://www.themoviedb.org/${type === '电影' ? 'movie' : 'tv'}/${tmdbId}`
|
|
||||||
window.open(url, '_blank')
|
|
||||||
}
|
|
||||||
|
|
||||||
// TMDB图片转换为w500大小
|
|
||||||
function getW500Image(url = '') {
|
|
||||||
if (!url)
|
|
||||||
return ''
|
|
||||||
return url.replace('original', 'w500')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -101,123 +86,7 @@ function getW500Image(url = '') {
|
|||||||
</VForm>
|
</VForm>
|
||||||
<VExpandTransition>
|
<VExpandTransition>
|
||||||
<div v-show="showResult">
|
<div v-show="showResult">
|
||||||
<VCol>
|
<MediaInfoCard :context="nameTestResult" />
|
||||||
<div
|
|
||||||
v-if="nameTestResult?.meta_info?.name"
|
|
||||||
class="d-flex justify-space-between flex-wrap flex-md-nowrap flex-column flex-md-row"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="nameTestResult?.media_info?.poster_path"
|
|
||||||
class="ma-auto"
|
|
||||||
>
|
|
||||||
<VImg
|
|
||||||
width="10rem"
|
|
||||||
aspect-ratio="2/3"
|
|
||||||
class="object-cover aspect-w-2 aspect-h-3 rounded-lg ring-1 ring-gray-500"
|
|
||||||
:src="getW500Image(nameTestResult?.media_info?.poster_path)"
|
|
||||||
cover
|
|
||||||
>
|
|
||||||
<template #placeholder>
|
|
||||||
<div class="w-full h-full">
|
|
||||||
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</VImg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<VCardItem class="pb-1">
|
|
||||||
<VCardTitle>
|
|
||||||
{{ nameTestResult?.media_info?.title || nameTestResult?.meta_info?.name }}
|
|
||||||
{{ nameTestResult?.meta_info?.season_episode }}
|
|
||||||
</VCardTitle>
|
|
||||||
<VCardSubtitle>
|
|
||||||
{{ nameTestResult?.media_info?.year || nameTestResult?.meta_info?.year }}
|
|
||||||
</VCardSubtitle>
|
|
||||||
</VCardItem>
|
|
||||||
|
|
||||||
<VCardText
|
|
||||||
v-if="nameTestResult?.media_info?.overview"
|
|
||||||
class="line-clamp-4 overflow-hidden text-ellipsis ..."
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.media_info?.overview }}
|
|
||||||
</VCardText>
|
|
||||||
|
|
||||||
<VCardItem>
|
|
||||||
<!-- 类型 -->
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.media_info?.type || nameTestResult?.meta_info?.type"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-blue-500"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
nameTestResult?.media_info?.type || nameTestResult?.meta_info?.type
|
|
||||||
}}
|
|
||||||
</VChip>
|
|
||||||
<!-- 二级分类 -->
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.media_info?.category"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-blue-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.media_info?.category }}
|
|
||||||
</VChip>
|
|
||||||
<!-- TMDBID -->
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.media_info?.tmdb_id"
|
|
||||||
variant="elevated"
|
|
||||||
color="success"
|
|
||||||
class="me-1 mb-1"
|
|
||||||
@click="openTmdbPage(nameTestResult?.media_info?.type || '', nameTestResult?.media_info?.tmdb_id)"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.media_info?.tmdb_id }}
|
|
||||||
</VChip>
|
|
||||||
<!-- meta_info -->
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.edition"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-red-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.edition }}
|
|
||||||
</VChip>
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.resource_pix"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-red-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.resource_pix }}
|
|
||||||
</VChip>
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.video_encode"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-orange-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.video_encode }}
|
|
||||||
</VChip>
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.audio_encode"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-orange-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.audio_encode }}
|
|
||||||
</VChip>
|
|
||||||
<VChip
|
|
||||||
v-if="nameTestResult?.meta_info?.resource_team"
|
|
||||||
variant="elevated"
|
|
||||||
class="me-1 mb-1 text-white bg-cyan-500"
|
|
||||||
>
|
|
||||||
{{ nameTestResult?.meta_info?.resource_team }}
|
|
||||||
</VChip>
|
|
||||||
</VCardItem>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<VAlert
|
|
||||||
v-if="!nameTestResult?.meta_info?.name"
|
|
||||||
icon="mdi-alert-circle-outline"
|
|
||||||
>
|
|
||||||
识别失败,无法识别到有效信息!
|
|
||||||
</VAlert>
|
|
||||||
</VCol>
|
|
||||||
</div>
|
</div>
|
||||||
</VExpandTransition>
|
</VExpandTransition>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user