仪表板组件支持拖动排序

This commit is contained in:
jxxghp
2024-05-09 14:45:12 +08:00
parent 74e96980e6
commit 2065992b17
18 changed files with 437 additions and 464 deletions

View File

@@ -6,8 +6,14 @@ 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)
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
@@ -22,83 +28,86 @@ const series = ref([
// 当前值
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,
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: {
lines: { show: false },
labels: { show: false },
axisTicks: { show: false },
axisBorder: { show: false },
},
yaxis: {
lines: { show: true },
labels: { show: false },
max: 100,
},
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
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) {
if (series.value[0].data.length > 30) series.value[0].data.shift()
} catch (e) {
console.log(e)
}
}
onMounted(() => {
getCpuUsage()// 启动定时器
getCpuUsage() // 启动定时器
refreshTimer = setInterval(() => {
getCpuUsage()
}, 2000)
@@ -115,20 +124,13 @@ onUnmounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle class="cursor-move">CPU</VCardTitle>
</VCardItem>
<VCardText>
<h6 class="text-h6">
CPU
</h6>
<VueApexCharts
type="line"
:options="chartOptions"
:series="series"
:height="150"
/>
<VueApexCharts type="line" :options="chartOptions" :series="series" :height="150" />
<p class="text-center font-weight-medium mb-0">
当前{{ current }}%
</p>
<p class="text-center font-weight-medium mb-0">当前{{ current }}%</p>
</VCardText>
</VCard>
</template>

View File

@@ -42,8 +42,7 @@ async function loadMediaStatistic() {
color: 'info',
},
]
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -56,29 +55,16 @@ onMounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle>媒体统计</VCardTitle>
<VCardTitle class="cursor-move">媒体统计</VCardTitle>
</VCardItem>
<VCardText>
<VRow>
<VCol
v-for="item in statistics"
:key="item.title"
cols="6"
sm="3"
>
<VCol v-for="item in statistics" :key="item.title" cols="6" sm="3">
<div class="d-flex align-center">
<div class="me-3">
<VAvatar
:color="item.color"
rounded
size="42"
class="elevation-1"
>
<VIcon
size="24"
:icon="item.icon"
/>
<VAvatar :color="item.color" rounded size="42" class="elevation-1">
<VIcon size="24" :icon="item.icon" />
</VAvatar>
</div>

View File

@@ -7,8 +7,14 @@ 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)
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
@@ -25,79 +31,82 @@ const usedMemory = ref(0)
// 内存使用百分比
const memoryUsage = 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,
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: {
lines: { show: false },
labels: { show: false },
axisTicks: { show: false },
axisBorder: { show: false },
},
yaxis: {
lines: { show: true },
labels: { show: false },
max: 100,
},
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 },
max: 100,
},
}
})
}
},
)
// 调用API接口获取最新内存使用量
async function getMemorgUsage() {
try {
// 请求数据
[usedMemory.value, memoryUsage.value] = await api.get('dashboard/memory')
;[usedMemory.value, memoryUsage.value] = await api.get('dashboard/memory')
series.value[0].data.push(memoryUsage.value)
// 序列超过30条记录时清掉前面的
if (series.value[0].data.length > 30)
series.value[0].data.shift()
}
catch (e) {
if (series.value[0].data.length > 30) series.value[0].data.shift()
} catch (e) {
console.log(e)
}
}
@@ -121,20 +130,13 @@ onUnmounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle class="cursor-move">内存</VCardTitle>
</VCardItem>
<VCardText>
<h6 class="text-h6">
内存
</h6>
<VueApexCharts
type="area"
:options="chartOptions"
:series="series"
:height="150"
/>
<VueApexCharts type="area" :options="chartOptions" :series="series" :height="150" />
<p class="text-center font-weight-medium mb-0">
当前{{ formatBytes(usedMemory) }}
</p>
<p class="text-center font-weight-medium mb-0">当前{{ formatBytes(usedMemory) }}</p>
</VCardText>
</VCard>
</template>

View File

@@ -18,8 +18,7 @@ async function loadProcessList() {
const res: Process[] = await api.get('dashboard/processes')
processList.value = res
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -43,47 +42,29 @@ onUnmounted(() => {
</script>
<template>
<VCard title="系统进程">
<VTable
item-key="fullName"
class="table-rounded"
hide-default-footer
disable-sort
>
<VCard>
<VCardItem>
<VCardTitle class="cursor-move">系统进程</VCardTitle>
</VCardItem>
<VTable item-key="fullName" class="table-rounded" hide-default-footer disable-sort>
<thead>
<tr>
<th
v-for="header in headers"
:id="header"
:key="header"
>
<th v-for="header in headers" :id="header" :key="header">
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="row in processList"
:key="row.pid"
>
<td
class="text-sm"
v-text="row.pid"
/>
<tr v-for="row in processList" :key="row.pid">
<td class="text-sm" v-text="row.pid" />
<!-- name -->
<td>
<h6 class="text-sm font-weight-medium">
{{ row.name }}
</h6>
</td>
<td
class="text-sm"
v-text="formatSeconds(row.run_time)"
/>
<td
class="text-sm"
v-text="`${row.memory} MB`"
/>
<td class="text-sm" v-text="formatSeconds(row.run_time)" />
<td class="text-sm" v-text="`${row.memory} MB`" />
</tr>
</tbody>
</VTable>

View File

@@ -14,8 +14,7 @@ async function loadSchedulerList() {
const res: ScheduleInfo[] = await api.get('dashboard/schedule')
schedulerList.value = res
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -41,25 +40,14 @@ onUnmounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle>后台任务</VCardTitle>
<VCardTitle class="cursor-move">后台任务</VCardTitle>
</VCardItem>
<VCardText>
<VList
class="card-list"
height="250"
>
<VListItem
v-for="item in schedulerList"
:key="item.id"
>
<VList class="card-list" height="250">
<VListItem v-for="item in schedulerList" :key="item.id">
<template #prepend>
<VAvatar
size="40"
variant="tonal"
color=""
class="me-3"
>
<VAvatar size="40" variant="tonal" color="" class="me-3">
{{ item.name[0] }}
</VAvatar>
</template>
@@ -81,9 +69,7 @@ onUnmounted(() => {
</template>
</VListItem>
<VListItem v-if="schedulerList.length === 0">
<VListItemTitle class="text-center">
没有后台服务
</VListItemTitle>
<VListItemTitle class="text-center"> 没有后台服务 </VListItemTitle>
</VListItem>
</VList>
</VCardText>

View File

@@ -56,8 +56,7 @@ async function loadDownloaderInfo() {
amount: formatFileSize(res.free_space),
},
]
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -83,28 +82,18 @@ onUnmounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle>实时速率</VCardTitle>
<VCardTitle class="cursor-move">实时速率</VCardTitle>
</VCardItem>
<VCardText class="pt-4">
<div>
<p class="text-h5 me-2">
{{ formatFileSize(downloadInfo.upload_speed) }}/s
</p>
<p class="text-h4 me-2">
{{ formatFileSize(downloadInfo.download_speed) }}/s
</p>
<p class="text-h5 me-2">{{ formatFileSize(downloadInfo.upload_speed) }}/s</p>
<p class="text-h4 me-2">{{ formatFileSize(downloadInfo.download_speed) }}/s</p>
</div>
<VList class="card-list mt-9">
<VListItem
v-for="item in infoItems"
:key="item.title"
>
<VListItem v-for="item in infoItems" :key="item.title">
<template #prepend>
<VIcon
rounded
:icon="item.avatar"
/>
<VIcon rounded :icon="item.avatar" />
</template>
<VListItemTitle class="text-sm font-weight-medium mb-1">

View File

@@ -8,9 +8,7 @@ import triangleLight from '@images/misc/triangle-light.png'
const { global } = useTheme()
const triangleBg = computed(() =>
global.name.value === 'light' ? triangleLight : triangleDark,
)
const triangleBg = computed(() => (global.name.value === 'light' ? triangleLight : triangleDark))
// 总存储空间
const storage = ref(0)
@@ -30,8 +28,7 @@ async function getStorage() {
storage.value = res.total_storage
used.value = res.used_storage
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -42,42 +39,30 @@ onMounted(() => {
</script>
<template>
<VCard
title="存储空间"
subtitle=""
class="position-relative"
>
<VCard>
<VCardItem>
<VCardTitle class="cursor-move">存储空间</VCardTitle>
</VCardItem>
<VCardText>
<h5 class="text-2xl font-weight-medium text-primary">
{{ formatFileSize(storage) }}
</h5>
<p class="mt-2">
已使用 {{ usedPercent }}% 🚀
</p>
<p class="mt-2">已使用 {{ usedPercent }}% 🚀</p>
<p class="mt-1">
<VProgressLinear
:model-value="usedPercent"
color="primary"
/>
<VProgressLinear :model-value="usedPercent" color="primary" />
</p>
</VCardText>
<!-- Triangle Background -->
<VImg
:src="triangleBg"
class="triangle-bg flip-in-rtl"
/>
<VImg :src="triangleBg" class="triangle-bg flip-in-rtl" />
<!-- Trophy -->
<VImg
:src="trophy"
class="trophy"
/>
<VImg :src="trophy" class="trophy" />
</VCard>
</template>
<style lang="scss">
@use "@layouts/styles/mixins" as layoutsMixins;
@use '@layouts/styles/mixins' as layoutsMixins;
.v-card .triangle-bg {
position: absolute;

View File

@@ -80,8 +80,7 @@ const options = controlledComputed(
fontSize: '12px',
},
formatter: (value: number) =>
value > 999 ? (value / 1000).toFixed(0) : value,
formatter: (value: number) => (value > 999 ? (value / 1000).toFixed(0) : value),
},
},
}
@@ -100,8 +99,7 @@ async function getWeeklyData() {
const res: number[] = await api.get('dashboard/transfer')
series.value = [{ data: res }]
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -114,16 +112,11 @@ onMounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle>最近入库</VCardTitle>
<VCardTitle class="cursor-move">最近入库</VCardTitle>
</VCardItem>
<VCardText>
<VueApexCharts
type="bar"
:options="options"
:series="series"
:height="160"
/>
<VueApexCharts type="bar" :options="options" :series="series" :height="160" />
<div class="d-flex align-center mb-3">
<h5 class="text-h5 me-4">
@@ -132,13 +125,7 @@ onMounted(() => {
<p>最近一周入库了 {{ totalCount }} 部影片 😎</p>
</div>
<VBtn
v-if="superUser"
block
to="/history"
>
查看详情
</VBtn>
<VBtn v-if="superUser" block to="/history"> 查看详情 </VBtn>
</VCardText>
</VCard>
</template>

View File

@@ -10,8 +10,7 @@ const latestList = ref<MediaServerPlayItem[]>([])
async function loadLatest() {
try {
latestList.value = await api.get('mediaserver/latest')
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -24,19 +23,11 @@ onMounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle>最近添加</VCardTitle>
<VCardTitle class="cursor-move">最近添加</VCardTitle>
</VCardItem>
<div
v-if="latestList.length > 0"
class="grid gap-4 grid-media-card mx-3 mb-3"
tabindex="0"
>
<PosterCard
v-for="data in latestList"
:key="data.id"
:media="data"
/>
<div v-if="latestList.length > 0" class="grid gap-4 grid-media-card mx-3 mb-3" tabindex="0">
<PosterCard v-for="data in latestList" :key="data.id" :media="data" />
</div>
</VCard>
</template>

View File

@@ -10,8 +10,7 @@ const libraryList = ref<MediaServerPlayItem[]>([])
async function loadLibrary() {
try {
libraryList.value = await api.get('mediaserver/library')
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -24,20 +23,11 @@ onMounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle>我的媒体库</VCardTitle>
<VCardTitle class="cursor-move">我的媒体库</VCardTitle>
</VCardItem>
<div
v-if="libraryList.length > 0"
class="grid gap-4 grid-backdrop-card mx-3"
tabindex="0"
>
<LibraryCard
v-for="data in libraryList"
:key="data.id"
:media="data"
height="10rem"
/>
<div v-if="libraryList.length > 0" class="grid gap-4 grid-backdrop-card mx-3" tabindex="0">
<LibraryCard v-for="data in libraryList" :key="data.id" :media="data" height="10rem" />
</div>
</VCard>
</template>

View File

@@ -10,8 +10,7 @@ const playingList = ref<MediaServerPlayItem[]>([])
async function loadPlayingList() {
try {
playingList.value = await api.get('mediaserver/playing')
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -24,20 +23,11 @@ onMounted(() => {
<template>
<VCard>
<VCardItem>
<VCardTitle>继续观看</VCardTitle>
<VCardTitle class="cursor-move">继续观看</VCardTitle>
</VCardItem>
<div
v-if="playingList.length > 0"
class="grid gap-4 grid-backdrop-card mx-3"
tabindex="0"
>
<BackdropCard
v-for="data in playingList"
:key="data.id"
:media="data"
height="10rem"
/>
<div v-if="playingList.length > 0" class="grid gap-4 grid-backdrop-card mx-3" tabindex="0">
<BackdropCard v-for="data in playingList" :key="data.id" :media="data" height="10rem" />
</div>
</VCard>
</template>