更新 1.3.0-beta24-01-09

Squashed commit of the following:

commit 53d1f382c5
Author: Sun <95302870@qq.com>
Date:   Tue Jan 9 11:54:33 2024 +0800

    系统状态标题加上阴影,beta版本最终优化

commit fc56328765
Author: Sun <95302870@qq.com>
Date:   Mon Jan 8 22:37:24 2024 +0800

    磁盘监控增加表单验证

commit 3905717d42
Author: Sun <95302870@qq.com>
Date:   Mon Jan 8 22:36:10 2024 +0800

    删除无用文件

commit 89b6b63310
Author: Sun <95302870@qq.com>
Date:   Mon Jan 8 21:10:10 2024 +0800

    修改翻译文件为json格式(为了方便引用插件)优化过期登录弹窗多个的问题

commit 2efb31571e
Author: Sun <95302870@qq.com>
Date:   Mon Jan 8 13:56:57 2024 +0800

    增加vscode工作区文件

commit bc79b661db
Author: Sun <95302870@qq.com>
Date:   Mon Jan 8 13:56:45 2024 +0800

    暂时解决依赖循环的问题

commit a24520f808
Author: Sun <95302870@qq.com>
Date:   Sun Jan 7 20:07:52 2024 +0800

    修改设置里面壁纸提示词错误的问题修改首页默认标题

commit 394c6ce20c
Author: Sun <95302870@qq.com>
Date:   Sun Jan 7 14:53:53 2024 +0800

    适配多语言
    Squashed commit of the following:

    commit 632f86c0228c68391c01865c7576f3aa0408c102
    Author: Sun <95302870@qq.com>
    Date:   Sun Jan 7 14:47:55 2024 +0800

        退出的时候清除appstore

    commit b9d805e49a3c6b2ad38bc8d527cb12cc8709012e
    Author: Sun <95302870@qq.com>
    Date:   Sun Jan 7 13:55:20 2024 +0800

        系统状态监控适配国际化

    commit daece99723ec96d210241d2ca4e5a85dc5ae69bd
    Author: Sun <95302870@qq.com>
    Date:   Sun Jan 7 13:09:46 2024 +0800

        适配添加项目页面的国际化配置还有时钟的星期*

    commit 8ea2b2fe951f6266415c96a197cb8d00faef4058
    Author: Sun <95302870@qq.com>
    Date:   Sun Jan 7 12:01:55 2024 +0800

        完成适配所有apps国际化

    commit 21ef54e0d4afb10f560c8cb7aff666374afe0f87
    Author: Sun <95302870@qq.com>
    Date:   Sat Jan 6 21:36:07 2024 +0800

        增加读取默认浏览器语言

    commit 6f710bbebe63ab2800193f27c71e5c0034f11978
    Author: Sun <95302870@qq.com>
    Date:   Sat Jan 6 21:09:58 2024 +0800

        登录页面增加语言选择选项

    commit cb7c4a89a160ed3ef91ad566ec98e75325e7601f
    Author: Sun <95302870@qq.com>
    Date:   Sat Jan 6 20:37:16 2024 +0800

        首次尝试增加英文语言,并在我的信息设置

    commit fb996e17cd11611d30c0e12feee00ddf7b225e32
    Author: Sun <95302870@qq.com>
    Date:   Sat Jan 6 18:22:40 2024 +0800

        完成基础设置页面的语言国际化适配

commit ffc378a38f
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 17:35:13 2024 +0800

    增加完善基本配置中的系统状态开关

commit c91eaf3e94
Merge: 7ebe358 a60f72c
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 12:57:05 2024 +0800

    Merge branch 'feature/monitor2' into dev

commit 7ebe35856e
Merge: d3e3cf5 779712a
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 12:56:15 2024 +0800

    Merge branch 'feature/footer' into dev

commit a60f72c277
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 12:55:04 2024 +0800

    优化了首页

commit 899c945fff
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 12:54:19 2024 +0800

    完成系统监控

commit cdf16277ff
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 12:41:38 2024 +0800

    增加删除功能

commit 128af005eb
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 11:56:32 2024 +0800

    完成排序保存功能

commit 3f4b3c67f2
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 11:46:59 2024 +0800

    初步完成了增改查包括磁盘状态

commit a85d90985d
Author: Sun <95302870@qq.com>
Date:   Fri Jan 5 22:19:47 2024 +0800

    增加磁盘信息卡片的适配

commit c955afd861
Author: Sun <95302870@qq.com>
Date:   Fri Jan 5 21:36:38 2024 +0800

    增加获取磁盘挂载点接口

commit 21e8e8f1b8
Author: Sun <95302870@qq.com>
Date:   Fri Jan 5 21:14:57 2024 +0800

    基础完成了增改查cpu和内存状态

commit bdbcd50aa1
Author: Sun <95302870@qq.com>
Date:   Fri Jan 5 14:25:09 2024 +0800

    优化公共入口组件

commit 9735e67a7d
Author: Sun <95302870@qq.com>
Date:   Fri Jan 5 14:01:46 2024 +0800

    适配三个组件

commit 3a82949afc
Author: Sun <95302870@qq.com>
Date:   Fri Jan 5 13:58:58 2024 +0800

    优化组件

commit 0d0421c8eb
Author: Sun <95302870@qq.com>
Date:   Fri Jan 5 13:27:29 2024 +0800

    初步完成了编辑器

commit 1474f796fc
Author: Sun <95302870@qq.com>
Date:   Thu Jan 4 16:22:33 2024 +0800

    完成大图标小图标切换

commit baf64a9272
Author: Sun <95302870@qq.com>
Date:   Thu Jan 4 12:08:39 2024 +0800

    增加获取各项监控的单独api

commit d3e3cf5d58
Author: Sun <95302870@qq.com>
Date:   Wed Jan 3 20:46:58 2024 +0800

    尝试将所有监控放在顶部

commit 8dfec7e4b7
Author: Sun <95302870@qq.com>
Date:   Wed Jan 3 20:02:03 2024 +0800

    完整横条显示并对容量尺寸单位优化自动识别

commit fe967a9314
Author: Sun <95302870@qq.com>
Date:   Wed Jan 3 18:55:39 2024 +0800

    适配显示了cpu、硬盘、内存信息

commit 11ea134be3
Author: Sun <95302870@qq.com>
Date:   Tue Jan 2 23:14:04 2024 +0800

    完成系统监控的基础api接口

commit c447884d77
Author: Sun <95302870@qq.com>
Date:   Tue Jan 2 22:11:34 2024 +0800

    完成基本的系统监控类库

commit 779712a5da
Author: Sun <95302870@qq.com>
Date:   Tue Jan 2 17:14:16 2024 +0800

    增加自定义footer
This commit is contained in:
Sun
2024-01-09 14:03:03 +08:00
parent 2e0af5f147
commit 4c1d67b513
62 changed files with 2696 additions and 397 deletions
+12 -3
View File
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { t } from '@/locales'
const props = defineProps<{
hideSecond?: boolean
@@ -35,7 +36,15 @@ function updateCurrentDate() {
const month = now.getMonth() + 1 // 月份从0开始,所以要加1
// const year = now.getFullYear()
const daysOfWeek = ['日', '一', '二', '三', '四', '五', '六']
const daysOfWeek = [
t('deskModule.clock.sun'),
t('deskModule.clock.mon'),
t('deskModule.clock.tue'),
t('deskModule.clock.wed'),
t('deskModule.clock.thu'),
t('deskModule.clock.fri'),
t('deskModule.clock.sat'),
]
// const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
currentDate.value.week = daysOfWeek[now.getDay()]
currentDate.value.date = `${month}-${day}`
@@ -63,11 +72,11 @@ onBeforeUnmount(() => {
{{ currentDate.time }}
</span>
<div class="hidden sm:hidden md:block">
<span>
<span class="mr-1">
{{ currentDate.date }}
</span>
<span>
星期{{ currentDate.week }}
{{ currentDate.week }}
</span>
</div>
</div>
@@ -0,0 +1,59 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import GenericProgress from '../components/GenericProgress/index.vue'
import { getCpuState } from '@/api/system/systemMonitor'
import type { PanelPanelConfigStyleEnum } from '@/enums'
interface Prop {
cardTypeStyle: PanelPanelConfigStyleEnum
refreshInterval: number
textColor: string
progressColor: string
progressRailColor: string
}
const props = defineProps<Prop>()
let timer: NodeJS.Timer
const cpuState = ref<SystemMonitor.CPUInfo | null>(null)
function correctionNumber(v: number, keepNum = 2): number {
return v === 0 ? 0 : Number(v.toFixed(keepNum))
}
async function getData() {
try {
const { data, code } = await getCpuState<SystemMonitor.CPUInfo>()
if (code === 0)
cpuState.value = data
}
catch (error) {
}
}
onMounted(() => {
getData()
timer = setInterval(() => {
getData()
}, (!props.refreshInterval || props.refreshInterval <= 2000) ? 2000 : props.refreshInterval)
})
onUnmounted(() => {
clearInterval(timer)
})
</script>
<template>
<div class="w-full">
<GenericProgress
:progress-color="progressColor"
:progress-rail-color="progressRailColor"
:progress-height="5"
:percentage="correctionNumber(cpuState?.usages[0] || 0)"
:card-type-style="cardTypeStyle"
:info-card-right-text="`${correctionNumber(cpuState?.usages[0] || 0)}%`"
info-card-left-text="CPU"
:text-color="textColor"
/>
</div>
</template>
@@ -0,0 +1,69 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import GenericProgress from '../components/GenericProgress/index.vue'
import type { PanelPanelConfigStyleEnum } from '@/enums'
import { bytesToSize } from '@/utils/cmn'
import { getDiskStateByPath } from '@/api/system/systemMonitor'
interface Prop {
cardTypeStyle: PanelPanelConfigStyleEnum
refreshInterval: number
textColor: string
progressColor: string
progressRailColor: string
path: string
}
const props = defineProps<Prop>()
let timer: NodeJS.Timer
const diskState = ref<SystemMonitor.DiskInfo | null>(null)
function correctionNumber(v: number, keepNum = 2): number {
return v === 0 ? 0 : Number(v.toFixed(keepNum))
}
function formatdiskSize(v: number): string {
return bytesToSize(v)
}
function formatdiskToByte(v: number): number {
return v
}
async function getData() {
try {
const { data, code } = await getDiskStateByPath<SystemMonitor.DiskInfo>(props.path)
if (code === 0)
diskState.value = data
}
catch (error) {
}
}
onMounted(() => {
getData()
timer = setInterval(() => {
getData()
}, (!props.refreshInterval || props.refreshInterval <= 2000) ? 2000 : props.refreshInterval)
})
onUnmounted(() => {
clearInterval(timer)
})
</script>
<template>
<div class="w-full">
<GenericProgress
:progress-color="progressColor"
:progress-rail-color="progressRailColor"
:progress-height="5"
:percentage="correctionNumber(diskState?.usedPercent || 0)"
:card-type-style="cardTypeStyle"
:info-card-right-text="`${formatdiskSize(formatdiskToByte(diskState?.total || 0))}/${formatdiskSize(formatdiskToByte(diskState?.free || 0))}`"
:info-card-left-text="diskState?.mountpoint"
:text-color="textColor"
/>
</div>
</template>
@@ -0,0 +1,64 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import GenericProgress from '../components/GenericProgress/index.vue'
import { getMemonyState } from '@/api/system/systemMonitor'
import type { PanelPanelConfigStyleEnum } from '@/enums'
import { bytesToSize } from '@/utils/cmn'
interface Prop {
cardTypeStyle: PanelPanelConfigStyleEnum
refreshInterval: number
textColor: string
progressColor: string
progressRailColor: string
}
const props = defineProps<Prop>()
let timer: NodeJS.Timer
const memoryState = ref<SystemMonitor.MemoryInfo | null>(null)
function correctionNumber(v: number, keepNum = 2): number {
return v === 0 ? 0 : Number(v.toFixed(keepNum))
}
function formatMemorySize(v: number): string {
return bytesToSize(v)
}
async function getData() {
try {
const { data, code } = await getMemonyState<SystemMonitor.MemoryInfo>()
if (code === 0)
memoryState.value = data
}
catch (error) {
}
}
onMounted(() => {
getData()
timer = setInterval(() => {
getData()
}, (!props.refreshInterval || props.refreshInterval <= 2000) ? 2000 : props.refreshInterval)
})
onUnmounted(() => {
clearInterval(timer)
})
</script>
<template>
<div class="w-full">
<GenericProgress
:progress-color="progressColor"
:progress-rail-color="progressRailColor"
:progress-height="5"
:percentage="correctionNumber(memoryState?.usedPercent || 0)"
:card-type-style="cardTypeStyle"
:info-card-right-text="`${formatMemorySize(memoryState?.total || 0)}/${formatMemorySize(memoryState?.free || 0)}`"
info-card-left-text="RAM"
:text-color="textColor"
/>
</div>
</template>
@@ -0,0 +1,118 @@
<script setup lang="ts">
import { computed } from 'vue'
import { MonitorType } from '../typings'
import type { CardStyle } from '../typings'
import GenericMonitorCard from '../components/GenericMonitorCard/index.vue'
import CardCPU from './CPU.vue'
import Memory from './Memory.vue'
import Disk from './Disk.vue'
import { SvgIcon } from '@/components/common'
import { PanelPanelConfigStyleEnum } from '@/enums'
interface Prop {
extendParam?: any
iconTextColor?: string
iconTextIconHideTitle: boolean
cardTypeStyle: PanelPanelConfigStyleEnum
monitorType: string
cardStyle: CardStyle
}
const props = withDefaults(defineProps<Prop>(), {})
const iconText = computed(() => {
switch (props.monitorType) {
case MonitorType.cpu:
return 'CPU'
case MonitorType.disk:
return props.extendParam.path
case MonitorType.memory:
return 'RAM'
}
return ''
})
const refreshInterval = 5000
</script>
<template>
<div class="w-full">
<GenericMonitorCard
:icon-text-icon-hide-title="false"
:card-type-style="cardTypeStyle"
:icon-text="iconText"
:icon-text-color="iconTextColor"
:background-color="extendParam.backgroundColor"
class="hover:shadow-[0_0_20px_10px_rgba(0,0,0,0.2)]"
>
<template #icon>
<!-- 图标 -->
<div class="w-[60px] h-[70px]">
<div class="w-[60px] h-full flex items-center justify-center text-white">
<SvgIcon v-if="monitorType === MonitorType.cpu" icon="solar-cpu-bold" :style="{ color: extendParam.color }" style="width:35px;height:35px" />
<SvgIcon v-if="monitorType === MonitorType.memory" icon="material-symbols-memory-alt-rounded" :style="{ color: extendParam.color }" style="width:35px;height:35px" />
<SvgIcon v-if="monitorType === MonitorType.disk" icon="clarity-hard-disk-solid" :style="{ color: extendParam.color }" style="width:35px;height:35px" />
</div>
</div>
</template>
<template #info>
<!-- 如果为纯白色,将自动根据背景的明暗计算字体的黑白色 -->
<div
class=" w-full text-white flex items-center"
>
<CardCPU
v-if="monitorType === MonitorType.cpu"
:card-type-style="PanelPanelConfigStyleEnum.info"
:progress-color="extendParam?.progressColor"
:progress-rail-color="extendParam?.progressRailColor"
:text-color="extendParam?.color"
:refresh-interval="refreshInterval"
/>
<Memory
v-else-if="monitorType === MonitorType.memory"
:card-type-style="PanelPanelConfigStyleEnum.info"
:progress-color="extendParam?.progressColor"
:progress-rail-color="extendParam?.progressRailColor"
:text-color="extendParam?.color"
:refresh-interval="refreshInterval"
/>
<Disk
v-else-if="monitorType === MonitorType.disk"
:card-type-style="PanelPanelConfigStyleEnum.info"
:progress-color="extendParam?.progressColor"
:progress-rail-color="extendParam?.progressRailColor"
:text-color="extendParam?.color"
:path="extendParam?.path"
:refresh-interval="refreshInterval"
/>
</div>
</template>
<template #small>
<CardCPU
v-if="monitorType === MonitorType.cpu"
:card-type-style="PanelPanelConfigStyleEnum.icon"
:progress-color="extendParam?.progressColor"
:progress-rail-color="extendParam?.progressRailColor"
:text-color="extendParam?.color"
:refresh-interval="refreshInterval"
/>
<Memory
v-else-if="monitorType === MonitorType.memory"
:card-type-style="PanelPanelConfigStyleEnum.icon"
:progress-color="extendParam?.progressColor"
:progress-rail-color="extendParam?.progressRailColor"
:text-color="extendParam?.color"
:refresh-interval="refreshInterval"
/>
<Disk
v-else-if="monitorType === MonitorType.disk"
:card-type-style="PanelPanelConfigStyleEnum.icon"
:progress-color="extendParam?.progressColor"
:progress-rail-color="extendParam?.progressRailColor"
:text-color="extendParam?.color"
:path="extendParam?.path"
:refresh-interval="refreshInterval"
/>
</template>
</GenericMonitorCard>
</div>
</template>
@@ -0,0 +1,163 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import type { FormInst, FormRules } from 'naive-ui'
import { NColorPicker, NForm, NFormItem, NSelect } from 'naive-ui'
import type { DiskExtendParam } from '../../typings'
import GenericMonitorCard from '../../components/GenericMonitorCard/index.vue'
import GenericProgress from '../../components/GenericProgress/index.vue'
import { PanelPanelConfigStyleEnum } from '@/enums'
import { getDiskMountpoints } from '@/api/system/systemMonitor'
import { defautSwatchesBackground } from '@/utils/defaultData'
import { t } from '@/locales'
interface Emit {
(e: 'update:diskExtendParam', visible: DiskExtendParam): void
}
const props = defineProps<{
diskExtendParam: DiskExtendParam
}>()
const emit = defineEmits<Emit>()
const formRef = ref<FormInst | null>(null)
const rules: FormRules = {
path: {
required: true,
message: t('form.required'),
trigger: 'blur',
},
}
const mountPointList = ref<{
label: string
value: string
}[]>([])
const extendParam = computed({
get: () => props.diskExtendParam,
set: (visible) => {
emit('update:diskExtendParam', visible)
},
})
async function getMountPointList() {
try {
const { data } = await getDiskMountpoints<SystemMonitor.Mountpoint[]>()
mountPointList.value = []
for (let i = 0; i < data.length; i++) {
const element = data[i]
if (i === 0 && !extendParam.value.path)
extendParam.value.path = element.mountpoint
mountPointList.value.push({
label: element.mountpoint,
value: element.mountpoint,
})
}
}
catch (error) {
}
}
onMounted(() => {
getMountPointList()
})
defineExpose({
async verification(): Promise<boolean> {
try {
const errors = await formRef.value?.validate()
if (!errors) {
return Promise.resolve(true)
}
else {
console.log(errors)
return Promise.resolve(false)
}
}
catch (error) {
console.error('An error occurred during validation:', error)
return Promise.resolve(false)
}
},
})
</script>
<template>
<div>
<!-- <div>{{ diskExtendParam }}</div> -->
<div class="flex mb-5 justify-center transparent-grid p-2 rounded-xl border">
<div class="w-[200px]">
<GenericMonitorCard
icon-text-icon-hide-title
:card-type-style="PanelPanelConfigStyleEnum.info"
icon="solar-cpu-bold"
:background-color="extendParam.backgroundColor"
:text-color="extendParam.color"
>
<template #info>
<GenericProgress
:progress-color="extendParam.progressColor"
:progress-rail-color="extendParam.progressRailColor"
:percentage="50"
:progress-height="5"
info-card-left-text="DEMO"
info-card-right-text="TEXT"
:text-color="extendParam.color"
:card-type-style="PanelPanelConfigStyleEnum.info"
/>
</template>
</GenericMonitorCard>
</div>
<div class="w-[80px] ml-2">
<GenericMonitorCard
icon-text-icon-hide-title
:card-type-style="PanelPanelConfigStyleEnum.icon"
icon="solar-cpu-bold"
:background-color="extendParam.backgroundColor"
:icon-text-color="extendParam.color"
>
<template #small>
<GenericProgress
:progress-color="extendParam.progressColor"
:progress-rail-color="extendParam.progressRailColor"
:percentage="50"
:text-color="extendParam.color"
:card-type-style="PanelPanelConfigStyleEnum.icon"
/>
</template>
</GenericMonitorCard>
</div>
</div>
<NForm ref="formRef" v-model="extendParam" :model="extendParam" :rules="rules">
<NFormItem :label="$t('deskModule.systemMonitor.diskMountPoint')" path="path">
<NSelect v-model:value="extendParam.path" size="small" :options="mountPointList" />
</NFormItem>
<NFormItem :label="$t('deskModule.systemMonitor.progressColor')">
<NColorPicker v-model:value="extendParam.progressColor" :swatches="defautSwatchesBackground" :modes="['hex']" size="small" />
</NFormItem>
<NFormItem :label="$t('deskModule.systemMonitor.progressRailColor')">
<NColorPicker v-model:value="extendParam.progressRailColor" :swatches="defautSwatchesBackground" :modes="['hex']" size="small" />
</NFormItem>
<NFormItem :label="$t('common.textColor')">
<NColorPicker v-model:value="extendParam.color" :swatches="defautSwatchesBackground" :modes="['hex']" size="small" />
</NFormItem>
<NFormItem :label="$t('common.backgroundColor')">
<NColorPicker v-model:value="extendParam.backgroundColor" :swatches="defautSwatchesBackground" :modes="['hex']" size="small" />
</NFormItem>
</NForm>
</div>
</template>
<style>
.transparent-grid {
background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%),
linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%);
background-size: 16px 16px;
background-position: 0 0, 8px 8px;
background-color: #e2e8f0;
}
</style>
@@ -0,0 +1,101 @@
<script setup lang="ts">
import { computed } from 'vue'
import { NColorPicker, NForm, NFormItem } from 'naive-ui'
import type { GenericProgressStyleExtendParam } from '../../typings'
import GenericMonitorCard from '../../components/GenericMonitorCard/index.vue'
import GenericProgress from '../../components/GenericProgress/index.vue'
import { PanelPanelConfigStyleEnum } from '@/enums'
import { defautSwatchesBackground } from '@/utils/defaultData'
interface Emit {
(e: 'update:genericProgressStyleExtendParam', visible: GenericProgressStyleExtendParam): void
}
const props = defineProps<{
genericProgressStyleExtendParam: GenericProgressStyleExtendParam
}>()
const emit = defineEmits<Emit>()
const data = computed({
get: () => props.genericProgressStyleExtendParam,
set: (visible) => {
emit('update:genericProgressStyleExtendParam', visible)
},
})
</script>
<template>
<div>
<!-- <div>{{ genericProgressStyleExtendParam }}</div>
<div>{{ data }}</div> -->
<div class="flex mb-5 justify-center transparent-grid p-2 rounded-xl border">
<div class="w-[200px]">
<GenericMonitorCard
icon-text-icon-hide-title
:card-type-style="PanelPanelConfigStyleEnum.info"
icon="solar-cpu-bold"
:background-color="data.backgroundColor"
:text-color="data.color"
>
<template #info>
<GenericProgress
:progress-color="data.progressColor"
:progress-rail-color="data.progressRailColor"
:percentage="50"
:progress-height="5"
info-card-left-text="DEMO"
info-card-right-text="TEXT"
:text-color="data.color"
:card-type-style="PanelPanelConfigStyleEnum.info"
/>
</template>
</GenericMonitorCard>
</div>
<div class="w-[80px] ml-2">
<GenericMonitorCard
icon-text-icon-hide-title
:card-type-style="PanelPanelConfigStyleEnum.icon"
icon="solar-cpu-bold"
:background-color="data.backgroundColor"
:icon-text-color="data.color"
>
<template #small>
<GenericProgress
:progress-color="data.progressColor"
:progress-rail-color="data.progressRailColor"
:percentage="50"
:text-color="data.color"
:card-type-style="PanelPanelConfigStyleEnum.icon"
/>
</template>
</GenericMonitorCard>
</div>
</div>
<NForm ref="formRef" v-model="data">
<NFormItem :label="$t('deskModule.systemMonitor.progressColor')">
<NColorPicker v-model:value="data.progressColor" :swatches="defautSwatchesBackground" :modes="['hex']" size="small" />
</NFormItem>
<NFormItem :label="$t('deskModule.systemMonitor.progressRailColor')">
<NColorPicker v-model:value="data.progressRailColor" :swatches="defautSwatchesBackground" :modes="['hex']" size="small" />
</NFormItem>
<NFormItem :label="$t('common.textColor')">
<NColorPicker v-model:value="data.color" :swatches="defautSwatchesBackground" :modes="['hex']" size="small" />
</NFormItem>
<NFormItem :label="$t('common.backgroundColor')">
<NColorPicker v-model:value="data.backgroundColor" :swatches="defautSwatchesBackground" :modes="['hex']" size="small" />
</NFormItem>
</NForm>
</div>
</template>
<style>
.transparent-grid {
background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%),
linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%);
background-size: 16px 16px;
background-position: 0 0, 8px 8px;
background-color: #e2e8f0;
}
</style>
@@ -0,0 +1,148 @@
<script setup lang="ts">
import { computed, defineEmits, defineProps, ref, watch } from 'vue'
import { NButton, NModal, NTabPane, NTabs, useMessage } from 'naive-ui'
import { MonitorType } from '../typings'
import type { DiskExtendParam, GenericProgressStyleExtendParam, MonitorData } from '../typings'
import { add, saveByIndex } from '../common'
import GenericProgressStyleEditor from './GenericProgressStyleEditor/index.vue'
import DiskEditor from './DiskEditor/index.vue'
import { t } from '@/locales'
interface Props {
visible: boolean
monitorData: MonitorData | null
index: number | null
}
const props = defineProps<Props>()
const emit = defineEmits<Emit>()
const DiskEditorRef = ref<InstanceType<typeof DiskEditor>>()
// 默认通用的进度扩展参数
const defaultGenericProgressStyleExtendParam: GenericProgressStyleExtendParam = {
progressColor: '#fff',
progressRailColor: '#CFCFCFA8',
color: '#fff',
backgroundColor: '#2a2a2a6b',
}
const defaultDiskExtendParam: DiskExtendParam = {
progressColor: '#fff',
progressRailColor: '#CFCFCFA8',
color: '#fff',
backgroundColor: '#2a2a2a6b',
path: '',
}
const defaultMonitorData: MonitorData = {
extendParam: defaultGenericProgressStyleExtendParam,
monitorType: MonitorType.cpu,
}
const active = ref<string>(MonitorType.cpu)
const currentMonitorData = ref<MonitorData>(props.monitorData || { ...defaultMonitorData })
const currentGenericProgressStyleExtendParam = ref<GenericProgressStyleExtendParam>({ ...defaultGenericProgressStyleExtendParam })
const currentDiskExtendParam = ref<DiskExtendParam>({ ...defaultDiskExtendParam })
const ms = useMessage()
const submitLoading = ref(false)
interface Emit {
(e: 'update:visible', visible: boolean): void
(e: 'done', item: boolean): void
}
// 更新值父组件传来的值
const show = computed({
get: () => props.visible,
set: (visible: boolean) => {
emit('update:visible', visible)
},
})
watch(() => props.visible, (value) => {
active.value = props.monitorData?.monitorType || MonitorType.cpu
if (props.monitorData?.monitorType === MonitorType.cpu || props.monitorData?.monitorType === MonitorType.memory)
currentGenericProgressStyleExtendParam.value = { ...props.monitorData?.extendParam }
else if (props.monitorData?.monitorType === MonitorType.disk)
currentDiskExtendParam.value = { ...props.monitorData?.extendParam }
if (!value)
handleResetExtendParam()
})
function handleResetExtendParam() {
currentGenericProgressStyleExtendParam.value = { ...defaultGenericProgressStyleExtendParam }
currentDiskExtendParam.value = { ...defaultDiskExtendParam }
}
// 保存提交
async function handleSubmit() {
let verificationRes = true
currentMonitorData.value.monitorType = active.value as MonitorType
if (currentMonitorData.value.monitorType === MonitorType.cpu || currentMonitorData.value.monitorType === MonitorType.memory) {
currentMonitorData.value.extendParam = currentGenericProgressStyleExtendParam
}
else if (currentMonitorData.value.monitorType === MonitorType.disk) {
currentMonitorData.value.extendParam = currentDiskExtendParam
const res = await DiskEditorRef.value?.verification()
if (res !== undefined)
verificationRes = res
}
// console.log('保存', currentMonitorData.value.extendParam)
if (!verificationRes)
return
if (props.index !== null) {
const res = await saveByIndex(props.index, currentMonitorData.value)
if (res) {
show.value = false
emit('done', true)
}
else {
ms.error(t('common.saveFail'))
}
}
else {
const res = await add(currentMonitorData.value)
if (res) {
show.value = false
emit('done', true)
}
else {
ms.error(t('common.saveFail'))
}
}
}
</script>
<template>
<NModal v-model:show="show" preset="card" size="small" style="width: 600px;border-radius: 1rem;" :title="monitorData ? t('common.edit') : t('common.add')">
<!-- 选择监视器 -->
<!-- <div>
{{ JSON.stringify(currentGenericProgressStyleExtendParam) }}
{{ JSON.stringify(currentDiskExtendParam) }}
</div> -->
<NTabs v-model:value="active" type="segment">
<NTabPane :name="MonitorType.cpu" :tab="$t('deskModule.systemMonitor.cpuState')">
<GenericProgressStyleEditor v-model:genericProgressStyleExtendParam="currentGenericProgressStyleExtendParam" />
</NTabPane>
<NTabPane :name="MonitorType.memory" :tab="$t('deskModule.systemMonitor.memoryState')">
<GenericProgressStyleEditor v-model:genericProgressStyleExtendParam="currentGenericProgressStyleExtendParam" />
</NTabPane>
<NTabPane :name="MonitorType.disk" :tab="$t('deskModule.systemMonitor.diskState')">
<DiskEditor ref="DiskEditorRef" v-model:disk-extend-param="currentDiskExtendParam" />
</NTabPane>
</NTabs>
<NButton @click="handleResetExtendParam">
{{ t('common.reset') }}
</NButton>
<template #footer>
<NButton type="success" :loading="submitLoading" style="float: right;" @click="handleSubmit">
{{ t('common.confirm') }}
</NButton>
</template>
</NModal>
</template>
@@ -0,0 +1,88 @@
import type { MonitorData } from './typings'
import { useModuleConfig } from '@/store/modules'
const modelName = 'systemMonitor'
const moduleConfig = useModuleConfig()
export async function saveAll(value: MonitorData[]) {
return await moduleConfig.saveToCloud(modelName, { list: value })
}
export async function getAll(): Promise< MonitorData[]> {
const res = await moduleConfig.getValueByNameFromCloud<{ list: MonitorData[] }>(modelName)
if (res.code === 0 && res.data && res.data.list)
return res.data.list
return []
}
export async function add(value: MonitorData): Promise<boolean> {
let success = true
let newData: MonitorData[] = []
try {
const data = await getAll()
if (data)
newData = data
newData.push(value)
const res = await saveAll(newData)
if (res.code !== 0)
console.log('save failed', res)
}
catch (error) {
console.error(error)
success = false
}
return success
}
export async function saveByIndex(index: number | undefined, value: MonitorData): Promise<boolean> {
if (!index)
index = 0
let success = true
let newData: MonitorData[] = []
try {
const data = await getAll()
if (data)
newData = data
newData[index] = value
const res = await saveAll(newData)
if (res.code !== 0)
console.log('save failed', res)
}
catch (error) {
console.error(error)
success = false
}
return success
}
export async function getByIndex(index: number): Promise<MonitorData | null> {
try {
const data = await getAll()
if (data[index])
return data[index]
}
catch (error) {
}
return null
}
export async function deleteByIndex(index: number): Promise<boolean> {
let success = true
try {
const data = await getAll()
if (data[index])
data.splice(index, 1)
await saveAll(data)
}
catch (error) {
success = false
console.error(error)
}
return success
}
@@ -0,0 +1,61 @@
<script setup lang="ts">
// -------------------
// 系统监控图标临时使用(与 AppIcon/index.vue 一致)
// 如果确定这种方案将 AppIcon/index.vue 封装成通用组件
// -------------------
import { ref } from 'vue'
import { ItemCard, SvgIcon } from '@/components/common'
import type { PanelPanelConfigStyleEnum } from '@/enums'
interface Prop {
// size?: number // 默认70
extendParam?: any
iconTextColor?: string
iconTextIconHideTitle?: boolean
iconText?: string
textColor?: string
cardTypeStyle: PanelPanelConfigStyleEnum
// monitorType: string
icon?: string
class?: string
backgroundColor?: string
}
const props = withDefaults(defineProps<Prop>(), {})
const propClass = ref(props.class)
</script>
<template>
<div class="w-full">
<ItemCard
:card-type-style="cardTypeStyle"
:icon-text="iconText"
:icon-text-color="iconTextColor"
:class="propClass"
:background-color="backgroundColor"
:icon-text-icon-hide-title="iconTextIconHideTitle"
>
<template #info>
<!-- 图标 -->
<div class="w-[60px] h-[70px]">
<div class="w-[60px] h-full flex items-center justify-center text-white">
<slot name="icon">
<SvgIcon :icon="icon ?? ''" style="width: 35px;height: 35px;" :style="{ color: textColor }" />
</slot>
</div>
</div>
<div
class=" w-full text-white flex items-center"
:style=" { maxWidth: 'calc(100% - 80px)' }"
>
<slot name="info" />
</div>
</template>
<template #small>
<slot name="small" />
</template>
</ItemCard>
</div>
</template>
@@ -0,0 +1,57 @@
<script setup lang="ts">
import { NProgress } from 'naive-ui'
import { PanelPanelConfigStyleEnum } from '@/enums'
interface Prop {
textColor: string
progressColor: string
progressRailColor: string
progressHeight?: number
percentage: number
cardTypeStyle: PanelPanelConfigStyleEnum
infoCardLeftText?: string
infoCardRightText?: string
}
defineProps<Prop>()
</script>
<template>
<div class="w-full">
<div v-if="cardTypeStyle === PanelPanelConfigStyleEnum.info">
<div class="mb-1 text-xs" :style="{ color: textColor }">
<span>
{{ infoCardLeftText }}
</span>
<span class="float-right">
{{ infoCardRightText }}
</span>
</div>
<NProgress
type="line"
:color="progressColor"
:rail-color="progressRailColor"
:height="progressHeight"
:percentage="percentage"
:show-indicator="false"
:stroke-width="15"
style="max-width: 135px;"
/>
</div>
<div v-else>
<div class="flex justify-center h-full w-full mt-3">
<NProgress
:color="progressColor"
:rail-color="progressRailColor"
type="dashboard"
:percentage="percentage" :stroke-width="15"
style="width: 50px;"
>
<div class="text-white" style="font-size: 8px;" :style="{ color: textColor }">
{{ percentage }}%
</div>
</NProgress>
</div>
</div>
</div>
</template>
@@ -0,0 +1,308 @@
<script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
import { NButton, NDropdown, useDialog, useMessage } from 'naive-ui'
import AppIconSystemMonitor from './AppIconSystemMonitor/index.vue'
import { type CardStyle, type MonitorData, MonitorType } from './typings'
import Edit from './Edit/index.vue'
import { deleteByIndex, getAll, saveAll } from './common'
import { usePanelState } from '@/store'
import { PanelPanelConfigStyleEnum } from '@/enums'
import { SvgIcon } from '@/components/common'
import { t } from '@/locales'
interface MonitorGroup extends Panel.ItemIconGroup {
sortStatus?: boolean
hoverStatus: boolean
items?: Panel.ItemInfo[]
}
const props = defineProps<{
allowEdit?: boolean
showTitle?: boolean
}>()
const panelState = usePanelState()
const dialog = useDialog()
const ms = useMessage()
const dropdownMenuX = ref(0)
const dropdownMenuY = ref(0)
const dropdownShow = ref(false)
const currentRightSelectIndex = ref<number | null>(null)
const monitorGroup = ref<MonitorGroup>({
hoverStatus: false,
sortStatus: false,
})
const editShowStatus = ref<boolean>(false)
const editData = ref<MonitorData | null>(null)
const editIndex = ref<number | null>(null)
function handleAddItem() {
editShowStatus.value = true
editData.value = null
editIndex.value = null
}
function handleSetSortStatus(sortStatus: boolean) {
monitorGroup.value.sortStatus = sortStatus
// 并未保存排序重新更新数据
if (!sortStatus)
getData()
}
function handleSetHoverStatus(hoverStatus: boolean) {
monitorGroup.value.hoverStatus = hoverStatus
}
const cardStyle: CardStyle = {
background: '#2a2a2a6b',
}
const monitorDatas = ref<MonitorData[]>([])
function handleClick(index: number, item: MonitorData) {
if (!props.allowEdit)
return
editShowStatus.value = true
editData.value = item
editIndex.value = index
}
async function getData() {
monitorDatas.value = await getAll()
if (monitorDatas.value.length === 0) {
// 防止空 - 默认数据
monitorDatas.value.push(
{
extendParam: {
backgroundColor: '#2a2a2a6b',
color: '#fff',
progressColor: '#fff',
progressRailColor: '#CFCFCFA8',
},
monitorType: MonitorType.cpu,
},
)
// 生成并保存
saveAll(monitorDatas.value)
}
}
onMounted(() => {
getData()
})
function handleSaveDone() {
getData()
}
async function handleSaveSort() {
const { code } = await saveAll(monitorDatas.value)
if (code === 0)
monitorGroup.value.sortStatus = false
}
function handleContextMenu(e: MouseEvent, index: number | null, item: MonitorData) {
if (index !== null) {
e.preventDefault()
currentRightSelectIndex.value = index
}
nextTick().then(() => {
dropdownShow.value = true
dropdownMenuX.value = e.clientX
dropdownMenuY.value = e.clientY
})
}
function getDropdownMenuOptions() {
const dropdownMenuOptions = [
{
label: t('common.delete'),
key: 'delete',
},
]
return dropdownMenuOptions
}
function onClickoutside() {
// message.info('clickoutside')
dropdownShow.value = false
}
async function deleteOneByIndex(index: number) {
const res = await deleteByIndex(index)
if (res)
getData()
}
function handleRightMenuSelect(key: string | number) {
dropdownShow.value = false
switch (key) {
case 'delete':
dialog.warning({
title: t('common.warning'),
content: t('common.deleteConfirm'),
positiveText: t('common.confirm'),
negativeText: t('common.cancel'),
onPositiveClick: () => {
if (monitorDatas.value.length <= 1) {
ms.warning(t('common.leastOne'))
return
}
if (currentRightSelectIndex.value !== null)
deleteOneByIndex(currentRightSelectIndex.value)
},
})
break
default:
break
}
}
</script>
<template>
<div class="w-full">
<div
class="mt-[50px]"
:class="monitorGroup.sortStatus ? 'shadow-2xl border shadow-[0_0_30px_10px_rgba(0,0,0,0.3)] p-[10px] rounded-2xl' : ''"
@mouseenter="handleSetHoverStatus(true)"
@mouseleave="handleSetHoverStatus(false)"
>
<!-- 分组标题 -->
<div class="text-white text-xl font-extrabold mb-[20px] ml-[10px] flex items-center">
<span v-if="showTitle" class="text-shadow">
{{ $t('deskModule.systemMonitor.systemState') }}
</span>
<div
v-if="allowEdit"
class="ml-2 delay-100 transition-opacity flex"
:class="monitorGroup.hoverStatus ? 'opacity-100' : 'opacity-0'"
>
<span class="mr-2 cursor-pointer" @click="handleAddItem()">
<SvgIcon class="text-white font-xl" icon="typcn:plus" />
</span>
<span class="mr-2 cursor-pointer" @click="handleSetSortStatus(!monitorGroup.sortStatus)">
<SvgIcon class="text-white font-xl" icon="ri:drag-drop-line" />
</span>
</div>
</div>
<!-- 详情图标 -->
<div v-if="panelState.panelConfig.iconStyle === PanelPanelConfigStyleEnum.info">
<VueDraggable
v-model="monitorDatas" item-key="sort" :animation="300"
class="icon-info-box"
filter=".not-drag"
:disabled="!monitorGroup.sortStatus"
>
<div
v-for="item, index in monitorDatas" :key="index"
:title="item.description"
@click="handleClick(index, item)"
@contextmenu="(e) => handleContextMenu(e, index, item)"
>
<AppIconSystemMonitor
:extend-param="item.extendParam"
:icon-text-icon-hide-title="false"
:card-type-style="panelState.panelConfig.iconStyle"
:monitor-type="item.monitorType"
:card-style="cardStyle"
:icon-text-color="panelState.panelConfig.iconTextColor"
/>
</div>
</VueDraggable>
</div>
<!-- APP图标宫型盒子 -->
<div v-if="panelState.panelConfig.iconStyle === PanelPanelConfigStyleEnum.icon">
<div v-if="monitorDatas">
<VueDraggable
v-model="monitorDatas" item-key="sort" :animation="300"
class="icon-small-box"
filter=".not-drag"
:disabled="!monitorGroup.sortStatus"
>
<div
v-for="item, index in monitorDatas" :key="index"
:title="item.description"
@click="handleClick(index, item)"
@contextmenu="(e) => handleContextMenu(e, index, item)"
>
<AppIconSystemMonitor
:extend-param="item.extendParam"
:icon-text-icon-hide-title="false"
:card-type-style="panelState.panelConfig.iconStyle"
:monitor-type="item.monitorType"
:card-style="cardStyle"
:icon-text-color="panelState.panelConfig.iconTextColor"
/>
</div>
</vuedraggable>
</div>
</div>
<!-- 编辑栏 -->
<div v-if="monitorGroup.sortStatus && allowEdit" class="flex mt-[10px]">
<div>
<NButton color="#2a2a2a6b" @click="handleSaveSort()">
<template #icon>
<SvgIcon class="text-white font-xl" icon="material-symbols:save" />
</template>
<div>
{{ $t('common.saveSort') }}
</div>
</NButton>
</div>
</div>
</div>
<Edit v-model:visible="editShowStatus" :monitor-data="editData" :index="editIndex" @done="handleSaveDone" />
<NDropdown
placement="bottom-start" trigger="manual" :x="dropdownMenuX" :y="dropdownMenuY"
:options="getDropdownMenuOptions()" :show="dropdownShow" :on-clickoutside="onClickoutside" @select="handleRightMenuSelect"
/>
</div>
</template>
<style scoped>
.text-shadow {
text-shadow: 2px 2px 50px rgb(0, 0, 0);
}
.app-icon-text-shadow {
text-shadow: 2px 2px 5px rgb(0, 0, 0);
}
.icon-info-box {
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 18px;
}
.icon-small-box {
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(75px, 1fr));
gap: 18px;
}
@media (max-width: 500px) {
.icon-info-box{
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
}
</style>
@@ -0,0 +1,33 @@
export enum MonitorType {
'cpu' = 'cpu', // 图标风格
'memory' = 'memory', // 详情风格
'disk' = 'disk',
}
export interface CardStyle {
background: string
}
export interface MonitorData {
monitorType: MonitorType
extendParam?: { [key: string]: [value:any] } | any
description?: string
// cardStyle: CardStyle
}
export interface ProgressStyle {
color: string
railColor: string
height: number
}
export interface GenericProgressStyleExtendParam {
progressColor: string
progressRailColor: string
color: string
backgroundColor: string
}
export interface DiskExtendParam extends GenericProgressStyleExtendParam {
path: string
}
+2 -1
View File
@@ -1,4 +1,5 @@
import Clock from './Clock/index.vue'
import SearchBox from './SearchBox/index.vue'
import SystemMonitor from './SystemMonitor/index.vue'
export { Clock, SearchBox }
export { Clock, SearchBox, SystemMonitor }