更新 1.3.0-beta24-01-09
Squashed commit of the following: commit53d1f382c5Author: Sun <95302870@qq.com> Date: Tue Jan 9 11:54:33 2024 +0800 系统状态标题加上阴影,beta版本最终优化 commitfc56328765Author: Sun <95302870@qq.com> Date: Mon Jan 8 22:37:24 2024 +0800 磁盘监控增加表单验证 commit3905717d42Author: Sun <95302870@qq.com> Date: Mon Jan 8 22:36:10 2024 +0800 删除无用文件 commit89b6b63310Author: Sun <95302870@qq.com> Date: Mon Jan 8 21:10:10 2024 +0800 修改翻译文件为json格式(为了方便引用插件)优化过期登录弹窗多个的问题 commit2efb31571eAuthor: Sun <95302870@qq.com> Date: Mon Jan 8 13:56:57 2024 +0800 增加vscode工作区文件 commitbc79b661dbAuthor: Sun <95302870@qq.com> Date: Mon Jan 8 13:56:45 2024 +0800 暂时解决依赖循环的问题 commita24520f808Author: Sun <95302870@qq.com> Date: Sun Jan 7 20:07:52 2024 +0800 修改设置里面壁纸提示词错误的问题修改首页默认标题 commit394c6ce20cAuthor: 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 完成基础设置页面的语言国际化适配 commitffc378a38fAuthor: Sun <95302870@qq.com> Date: Sat Jan 6 17:35:13 2024 +0800 增加完善基本配置中的系统状态开关 commitc91eaf3e94Merge:7ebe358a60f72cAuthor: Sun <95302870@qq.com> Date: Sat Jan 6 12:57:05 2024 +0800 Merge branch 'feature/monitor2' into dev commit7ebe35856eMerge:d3e3cf5779712aAuthor: Sun <95302870@qq.com> Date: Sat Jan 6 12:56:15 2024 +0800 Merge branch 'feature/footer' into dev commita60f72c277Author: Sun <95302870@qq.com> Date: Sat Jan 6 12:55:04 2024 +0800 优化了首页 commit899c945fffAuthor: Sun <95302870@qq.com> Date: Sat Jan 6 12:54:19 2024 +0800 完成系统监控 commitcdf16277ffAuthor: Sun <95302870@qq.com> Date: Sat Jan 6 12:41:38 2024 +0800 增加删除功能 commit128af005ebAuthor: Sun <95302870@qq.com> Date: Sat Jan 6 11:56:32 2024 +0800 完成排序保存功能 commit3f4b3c67f2Author: Sun <95302870@qq.com> Date: Sat Jan 6 11:46:59 2024 +0800 初步完成了增改查包括磁盘状态 commita85d90985dAuthor: Sun <95302870@qq.com> Date: Fri Jan 5 22:19:47 2024 +0800 增加磁盘信息卡片的适配 commitc955afd861Author: Sun <95302870@qq.com> Date: Fri Jan 5 21:36:38 2024 +0800 增加获取磁盘挂载点接口 commit21e8e8f1b8Author: Sun <95302870@qq.com> Date: Fri Jan 5 21:14:57 2024 +0800 基础完成了增改查cpu和内存状态 commitbdbcd50aa1Author: Sun <95302870@qq.com> Date: Fri Jan 5 14:25:09 2024 +0800 优化公共入口组件 commit9735e67a7dAuthor: Sun <95302870@qq.com> Date: Fri Jan 5 14:01:46 2024 +0800 适配三个组件 commit3a82949afcAuthor: Sun <95302870@qq.com> Date: Fri Jan 5 13:58:58 2024 +0800 优化组件 commit0d0421c8ebAuthor: Sun <95302870@qq.com> Date: Fri Jan 5 13:27:29 2024 +0800 初步完成了编辑器 commit1474f796fcAuthor: Sun <95302870@qq.com> Date: Thu Jan 4 16:22:33 2024 +0800 完成大图标小图标切换 commitbaf64a9272Author: Sun <95302870@qq.com> Date: Thu Jan 4 12:08:39 2024 +0800 增加获取各项监控的单独api commitd3e3cf5d58Author: Sun <95302870@qq.com> Date: Wed Jan 3 20:46:58 2024 +0800 尝试将所有监控放在顶部 commit8dfec7e4b7Author: Sun <95302870@qq.com> Date: Wed Jan 3 20:02:03 2024 +0800 完整横条显示并对容量尺寸单位优化自动识别 commitfe967a9314Author: Sun <95302870@qq.com> Date: Wed Jan 3 18:55:39 2024 +0800 适配显示了cpu、硬盘、内存信息 commit11ea134be3Author: Sun <95302870@qq.com> Date: Tue Jan 2 23:14:04 2024 +0800 完成系统监控的基础api接口 commitc447884d77Author: Sun <95302870@qq.com> Date: Tue Jan 2 22:11:34 2024 +0800 完成基本的系统监控类库 commit779712a5daAuthor: Sun <95302870@qq.com> Date: Tue Jan 2 17:14:16 2024 +0800 增加自定义footer
This commit is contained in:
@@ -35,7 +35,7 @@ onMounted(() => {
|
||||
</div>
|
||||
<div class="text-xl">
|
||||
<NGradientText type="info">
|
||||
<a href="https://github.com/hslr-s/sun-panel/releases" class="font-semibold" title="点此查看更新说明" target="_blank">v{{ versionName }}</a>
|
||||
<a href="https://github.com/hslr-s/sun-panel/releases" class="font-semibold" :title="$t('apps.about.viewUpdateLog')" target="_blank">v{{ versionName }}</a>
|
||||
</NGradientText>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,19 +43,23 @@ onMounted(() => {
|
||||
<NDivider> • </NDivider>
|
||||
<div class="flex flex-col items-center justify-center text-base">
|
||||
<div>
|
||||
建议反馈:<a href="https://github.com/hslr-s/sun-panel/issues" target="_blank" class="link">Github Issues</a>
|
||||
<a href="https://github.com/hslr-s/sun-panel/releases" target="_blank" class="link">{{ $t('apps.about.checkUpdate') }}</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
QQ交流群:<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=_I9WIoJn1roIdoaAqelSj9qClLKlXIa1&authKey=GfsQP2GagHnus0jMc7U8Sm6VhWjtsipXUzCHbFwQsGyHMgmYWx6ZbAP%2Bhut%2B4D6N&noverify=0&group_code=276594668" target="_blank" class="link">276594668</a>
|
||||
{{ $t('apps.about.issue') }}<a href="https://github.com/hslr-s/sun-panel/issues" target="_blank" class="link">Github Issues</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ $t('apps.about.QQGroup') }}<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=_I9WIoJn1roIdoaAqelSj9qClLKlXIa1&authKey=GfsQP2GagHnus0jMc7U8Sm6VhWjtsipXUzCHbFwQsGyHMgmYWx6ZbAP%2Bhut%2B4D6N&noverify=0&group_code=276594668" target="_blank" class="link">{{ $t("apps.about.addQQGroupUrl") }}</a>
|
||||
|
|
||||
<span class="link cursor-pointer" @click="qqGroupQRShow = !qqGroupQRShow">
|
||||
二维码(推荐)
|
||||
{{ $t('apps.about.QR') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
开发者:<a href="https://blog.enianteam.com/u/sun/content/11" target="_blank" class="link">红烧猎人</a> | <a href="https://github.com/hslr-s/sun-panel/blob/master/doc/donate.md" target="_blank" class="text-red-600 hover:text-red-900">🧧打赏</a>
|
||||
{{ $t('apps.about.author') }}<a href="https://github.com/hslr-s" target="_blank" class="link">红烧猎人</a> | <a href="https://github.com/hslr-s/sun-panel/blob/master/doc/donate.md" target="_blank" class="text-red-600 hover:text-red-900">{{ $t('apps.about.donate') }}</a>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-[10px]">
|
||||
|
||||
@@ -91,9 +91,9 @@ async function importIcons(): Promise<string | null> {
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error)
|
||||
return `发生错误: ${error.message}`
|
||||
return `${t('common.failed')}: ${error.message}`
|
||||
else
|
||||
return '发生未知错误'
|
||||
return t('common.unknownError')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ function handleFileChange(options: { file: UploadFileInfo; fileList: Array<Uploa
|
||||
importCheck()
|
||||
}
|
||||
else {
|
||||
ms.error('异常请重新上传')
|
||||
ms.error(`${t('common.failed')}: ${t('common.repeatLater')}`)
|
||||
}
|
||||
uploadLoading.value = false
|
||||
}
|
||||
@@ -178,13 +178,13 @@ function importCheck() {
|
||||
importObj.value = importJsonString(jsonData.value)
|
||||
if (importObj.value) {
|
||||
if (!importObj.value.isPassCheckMd5())
|
||||
importWarning.value.push('文件被修改过,谨慎导入')
|
||||
importWarning.value.push(t('apps.exportImport.fileModified'))
|
||||
|
||||
if (!importObj.value.isPassCheckConfigVersionOld())
|
||||
importWarning.value.push('配置文件版本过低,但是兼容')
|
||||
importWarning.value.push(t('apps.exportImport.warnConfigFileLow'))
|
||||
|
||||
if (!importObj.value.isPassCheckConfigVersionNew())
|
||||
importWarning.value.push('当前软件版本可能过旧,很有可能无法兼容该配置文件,请谨慎导入。推荐将软件更新到新版后再次导入')
|
||||
importWarning.value.push(t('apps.exportImport.softwareVersionLow'))
|
||||
|
||||
// (暂时不做)此处可以判断,当前的配置文件是否存在的导入项目(不存在隐藏importItems里面的值)操作变量:importItems
|
||||
|
||||
@@ -196,24 +196,24 @@ function importCheck() {
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof ConfigVersionLowError) {
|
||||
ms.error('配置文件版本过低,无法兼容')
|
||||
console.log('配置文件版本过低')
|
||||
ms.error(t('apps.exportImport.errorConfigFileLow'))
|
||||
console.error('The configuration file version is too low to be compatible')
|
||||
}
|
||||
else if (error instanceof FormatError) {
|
||||
ms.error('格式不正确,无法导入')
|
||||
console.log('格式不正确')
|
||||
ms.error(t('apps.exportImport.errorConfigFileFormat'))
|
||||
console.error('The format is incorrect and cannot be imported')
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ms.error('数据不正确')
|
||||
ms.error(t('apps.exportImport.errorConfigFileFormat'))
|
||||
}
|
||||
}
|
||||
|
||||
// 开始导出
|
||||
async function handleStartExport() {
|
||||
loading.value = true
|
||||
console.log('要导出的项目', checkedItems.value)
|
||||
// console.log('要导出的项目', checkedItems.value)
|
||||
// 获取软件版本号
|
||||
const exportResult = exportJson(version.value)
|
||||
if (checkedItems.value.includes('icons')) {
|
||||
@@ -223,7 +223,7 @@ async function handleStartExport() {
|
||||
console.log('export icons finish', iconGroups)
|
||||
}
|
||||
|
||||
console.log('导出结果')
|
||||
// console.log('导出结果')
|
||||
|
||||
jsonData.value = exportResult.string()
|
||||
exportResult.exportFile()
|
||||
@@ -244,14 +244,14 @@ async function handleStartImport() {
|
||||
|
||||
loading.value = false
|
||||
importRoundModalShow.value = false
|
||||
ms.success(`${t('common.success')},请手动刷新页面`)
|
||||
ms.success(`${t('common.success')}, ${t('common.refreshPage')}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pt-2">
|
||||
<NAlert type="info" :bordered="false">
|
||||
<p>导入图标配置数据不会清空现有图标数据</p>
|
||||
<p>{{ $t('apps.exportImport.tip') }}</p>
|
||||
</NAlert>
|
||||
<div class="flex justify-center m-[50px]">
|
||||
<div class="m-[10px]">
|
||||
@@ -266,7 +266,7 @@ async function handleStartImport() {
|
||||
<template #icon>
|
||||
<SvgIcon icon="fa6:solid-file-import" />
|
||||
</template>
|
||||
导入配置
|
||||
{{ $t('apps.exportImport.import') }}
|
||||
</NButton>
|
||||
</NUpload>
|
||||
</div>
|
||||
@@ -275,13 +275,13 @@ async function handleStartImport() {
|
||||
<template #icon>
|
||||
<SvgIcon icon="fa6:solid-file-export" />
|
||||
</template>
|
||||
导出配置
|
||||
{{ $t('apps.exportImport.export') }}
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<a href="https://hslr-s.github.io/sun-panel-tool-page/#/" target="_blank">浏览器书签转换工具</a>
|
||||
<a href="https://hslr-s.github.io/sun-panel-tool-page/#/" target="_blank">{{ $t('apps.exportImport.transmuteStandard') }}</a>
|
||||
</div>
|
||||
|
||||
<!-- 调试模式 -->
|
||||
@@ -310,7 +310,7 @@ async function handleStartImport() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RoundCardModal v-model:show="importRoundModalShow" style="max-width: 400px;" title="导入">
|
||||
<RoundCardModal v-model:show="importRoundModalShow" style="max-width: 400px;" :title=" $t('apps.exportImport.import')">
|
||||
<div v-if="importWarning.length > 0">
|
||||
<NAlert :title="$t('common.warning')" type="warning">
|
||||
<div v-for="(text, index) in importWarning " :key="index">
|
||||
@@ -319,39 +319,39 @@ async function handleStartImport() {
|
||||
</NAlert>
|
||||
</div>
|
||||
<NDivider title-placement="left">
|
||||
请选择要导入的配置数据
|
||||
{{ $t('apps.exportImport.selectImportData') }}
|
||||
</NDivider>
|
||||
|
||||
<NSpace justify="center" style="margin-top: 20px;">
|
||||
<NCheckboxGroup v-model:value="checkedItems">
|
||||
<NCheckbox v-if="importItems.includes('icons')" value="icons" label="图标" />
|
||||
<NCheckbox v-if="importItems.includes('style')" value="style" label="样式配置" />
|
||||
<NCheckbox v-if="importItems.includes('icons')" value="icons" :label="$t('apps.exportImport.moduleIcon')" />
|
||||
<NCheckbox v-if="importItems.includes('style')" value="style" :label="$t('apps.exportImport.moduleStyle')" />
|
||||
</NCheckboxGroup>
|
||||
</NSpace>
|
||||
<NSpace justify="center">
|
||||
<div class="mt-[50px]">
|
||||
<NButton type="success" :disabled="checkedItems.length === 0" :loading="loading" @click="handleStartImport">
|
||||
继续导入
|
||||
{{ $t('common.continue') }}
|
||||
</NButton>
|
||||
</div>
|
||||
</NSpace>
|
||||
</RoundCardModal>
|
||||
|
||||
<RoundCardModal v-model:show="exportRoundModalShow" style="max-width: 400px;" title="导出">
|
||||
<RoundCardModal v-model:show="exportRoundModalShow" style="max-width: 400px;" :title=" $t('apps.exportImport.export')">
|
||||
<NDivider title-placement="left">
|
||||
请选择要导出的配置数据
|
||||
{{ $t('apps.exportImport.selectExportData') }}
|
||||
</NDivider>
|
||||
|
||||
<NSpace justify="center" style="margin-top: 20px;">
|
||||
<NCheckboxGroup v-model:value="checkedItems">
|
||||
<NCheckbox v-if="importItems.includes('icons')" value="icons" label="图标" />
|
||||
<NCheckbox v-if="importItems.includes('style')" value="style" label="样式配置" />
|
||||
<NCheckbox v-if="importItems.includes('icons')" value="icons" :label="$t('apps.exportImport.moduleIcon')" />
|
||||
<NCheckbox v-if="importItems.includes('style')" value="style" :label="$t('apps.exportImport.moduleStyle')" />
|
||||
</NCheckboxGroup>
|
||||
</NSpace>
|
||||
<NSpace justify="center">
|
||||
<div class="mt-[50px]">
|
||||
<NButton type="success" :disabled="checkedItems.length === 0" :loading="loading" @click="handleStartExport">
|
||||
继续导出
|
||||
{{ $t('common.continue') }}
|
||||
</NButton>
|
||||
</div>
|
||||
</NSpace>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { NButton, NCard, NForm, NFormItem, NInput, useDialog, useMessage } from
|
||||
import { VueDraggable } from 'vue-draggable-plus'
|
||||
import { deletes, edit, getList, saveSort } from '@/api/panel/itemIconGroup'
|
||||
import { RoundCardModal, SvgIcon } from '@/components/common'
|
||||
import { t } from '@/locales'
|
||||
|
||||
interface EditModalArg {
|
||||
show: boolean
|
||||
@@ -33,7 +34,7 @@ const editModalArg = ref<EditModalArg>({
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
message: '必填项',
|
||||
message: t('form.required'),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -66,26 +67,26 @@ function handleSaveSort() {
|
||||
}
|
||||
saveSort(saveItems).then(({ code, msg }) => {
|
||||
if (code === 0) {
|
||||
ms.success('保存成功')
|
||||
ms.success(t('common.saveSuccess'))
|
||||
sortStatus.value = false
|
||||
}
|
||||
else {
|
||||
ms.error(`保存失败:${msg}`)
|
||||
ms.error(`${t('common.saveFail')}:${msg}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(groupInfo: Panel.ItemIconGroup) {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: `你确定删除此分组[ ${groupInfo.title} ],删除后此分组应用图标将丢失?`,
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
title: t('common.warning'),
|
||||
content: t('apps.itemGroupManage.deleteWarnText', { name: groupInfo.title }),
|
||||
positiveText: t('common.confirm'),
|
||||
negativeText: t('common.cancel'),
|
||||
onPositiveClick: () => {
|
||||
if (groupInfo.id) {
|
||||
deletes([groupInfo.id]).then(({ code, msg }) => {
|
||||
if (code !== 0)
|
||||
ms.error(`删除失败:${msg}`)
|
||||
ms.error(t('common.deleteFail'))
|
||||
else
|
||||
refreshList()
|
||||
})
|
||||
@@ -126,15 +127,15 @@ onMounted(() => {
|
||||
<div class="h-full">
|
||||
<div class="p-2">
|
||||
<NButton type="success" size="small" style="margin-right: 10px;" @click="handleAddGroup">
|
||||
新增分组
|
||||
{{ $t('common.add') }}
|
||||
</NButton>
|
||||
|
||||
<NButton v-if="!sortStatus" size="small" @click="handleDragSort">
|
||||
排序
|
||||
{{ $t('common.sort') }}
|
||||
</NButton>
|
||||
|
||||
<NButton v-else type="warning" size="small" @click="handleSaveSort">
|
||||
保存排序
|
||||
{{ $t('common.saveSort') }}
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
@@ -181,8 +182,8 @@ onMounted(() => {
|
||||
|
||||
<RoundCardModal v-model:show="editModalArg.show" size="small" type="small" :title="editModalArg.editStatus === 1 ? '添加' : '编辑'" style="width: 400px;">
|
||||
<NForm ref="formRef" :model="editModalArg.model" :rules="editModalArg.rules">
|
||||
<NFormItem path="title" label="分组名称">
|
||||
<NInput v-model:value="editModalArg.model.title" type="text" :maxlength="20" show-count placeholder="请输入" />
|
||||
<NFormItem path="title" :label="$t('apps.itemGroupManage.groupName')">
|
||||
<NInput v-model:value="editModalArg.model.title" type="text" :maxlength="20" show-count />
|
||||
</NFormItem>
|
||||
|
||||
<!-- <NFormItem path="name" label="昵称">
|
||||
@@ -191,7 +192,7 @@ onMounted(() => {
|
||||
</NForm>
|
||||
<template #footer>
|
||||
<NButton type="success" size="small" class="float-right" @click="handleSaveGroup">
|
||||
确定
|
||||
{{ $t('common.confirm') }}
|
||||
</NButton>
|
||||
</template>
|
||||
</RoundCardModal>
|
||||
|
||||
@@ -5,20 +5,22 @@ import { NButton, NCard, NColorPicker, NGrid, NGridItem, NInput, NInputGroup, NP
|
||||
import { useAuthStore, usePanelState } from '@/store'
|
||||
import { set as setUserConfig } from '@/api/panel/userConfig'
|
||||
import { PanelPanelConfigStyleEnum } from '@/enums/panel'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const panelState = usePanelState()
|
||||
const ms = useMessage()
|
||||
const showWallpaperInput = ref(false)
|
||||
|
||||
const isSaveing = ref(false)
|
||||
|
||||
const iconTypeOptions = [
|
||||
{
|
||||
label: '详情图标',
|
||||
label: t('apps.baseSettings.detailIcon'),
|
||||
value: PanelPanelConfigStyleEnum.info,
|
||||
},
|
||||
{
|
||||
label: '小图标',
|
||||
label: t('apps.baseSettings.smallIcon'),
|
||||
value: PanelPanelConfigStyleEnum.icon,
|
||||
},
|
||||
]
|
||||
@@ -61,9 +63,9 @@ function handleUploadBackgroundFinish({
|
||||
function uploadCloud() {
|
||||
setUserConfig({ panel: panelState.panelConfig }).then((res) => {
|
||||
if (res.code === 0)
|
||||
ms.success('配置已保存')
|
||||
ms.success(t('apps.baseSettings.configSaved'))
|
||||
else
|
||||
ms.error(`配置已保存${res.msg}`)
|
||||
ms.error(t('apps.baseSettings.configFailed', { message: res.msg }))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,7 +84,7 @@ function resetPanelConfig() {
|
||||
|
||||
<div>
|
||||
<div>
|
||||
文本内容
|
||||
{{ $t('apps.baseSettings.textContent') }}
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NInput v-model:value="panelState.panelConfig.logoText" type="text" show-count :maxlength="20" placeholder="请输入文字" />
|
||||
@@ -92,35 +94,53 @@ function resetPanelConfig() {
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px] font-bold">
|
||||
时钟
|
||||
{{ $t('apps.baseSettings.clock') }}
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">显示秒</span>
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.clockSecondShow') }}</span>
|
||||
<NSwitch v-model:value="panelState.panelConfig.clockShowSecond" />
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px] font-bold">
|
||||
搜索框
|
||||
{{ $t('apps.baseSettings.searchBar') }}
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">显示</span>
|
||||
<span class="mr-[10px]">{{ $t('common.show') }}</span>
|
||||
<NSwitch v-model:value="panelState.panelConfig.searchBoxShow" />
|
||||
</div>
|
||||
<div v-if="panelState.panelConfig.searchBoxShow" class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">允许搜索快捷图标</span>
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.searchBarSearchItem') }}</span>
|
||||
<NSwitch v-model:value="panelState.panelConfig.searchBoxSearchIcon" />
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px] font-bold">
|
||||
图标
|
||||
{{ $t('apps.baseSettings.systemMonitorStatus') }}
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">{{ $t('common.show') }}</span>
|
||||
<NSwitch v-model:value="panelState.panelConfig.systemMonitorShow" />
|
||||
</div>
|
||||
<div v-if="panelState.panelConfig.systemMonitorShow" class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.showTitle') }}</span>
|
||||
<NSwitch v-model:value="panelState.panelConfig.systemMonitorShowTitle" />
|
||||
</div>
|
||||
<div v-if="panelState.panelConfig.systemMonitorShow" class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.publicVisitModeShow') }}</span>
|
||||
<NSwitch v-model:value="panelState.panelConfig.systemMonitorPublicVisitModeShow" />
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px] font-bold">
|
||||
{{ $t('common.icon') }}
|
||||
</div>
|
||||
<div class="mt-[5px]">
|
||||
<div>
|
||||
样式
|
||||
{{ $t('common.style') }}
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NSelect v-model:value="panelState.panelConfig.iconStyle" :options="iconTypeOptions" />
|
||||
@@ -129,7 +149,7 @@ function resetPanelConfig() {
|
||||
|
||||
<div v-if="panelState.panelConfig.iconStyle === PanelPanelConfigStyleEnum.info" class="mt-[5px]">
|
||||
<div>
|
||||
隐藏描述信息
|
||||
{{ $t('apps.baseSettings.hideDescription') }}
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NSwitch v-model:value="panelState.panelConfig.iconTextInfoHideDescription" />
|
||||
@@ -138,7 +158,7 @@ function resetPanelConfig() {
|
||||
|
||||
<div v-if="panelState.panelConfig.iconStyle === PanelPanelConfigStyleEnum.icon" class="mt-[5px]">
|
||||
<div>
|
||||
隐藏标题
|
||||
{{ $t('apps.baseSettings.hideTitle') }}
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NSwitch v-model:value="panelState.panelConfig.iconTextIconHideTitle" />
|
||||
@@ -147,7 +167,7 @@ function resetPanelConfig() {
|
||||
|
||||
<div class="mt-[5px]">
|
||||
<div>
|
||||
文字颜色
|
||||
{{ $t('common.textColor') }}
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NColorPicker
|
||||
@@ -168,7 +188,7 @@ function resetPanelConfig() {
|
||||
</NCard>
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px] font-bold">
|
||||
壁纸
|
||||
{{ $t('apps.baseSettings.wallpaper') }}
|
||||
</div>
|
||||
<NUpload
|
||||
action="/api/file/uploadImg"
|
||||
@@ -186,32 +206,40 @@ function resetPanelConfig() {
|
||||
:style="{ background: `url(${panelState.panelConfig.backgroundImageSrc}) no-repeat`, backgroundSize: 'cover' }"
|
||||
>
|
||||
<div class="text-shadow text-white">
|
||||
点击上传替换图片或拖拽到框内
|
||||
{{ $t('apps.baseSettings.uploadOrDragText') }}
|
||||
</div>
|
||||
</div>
|
||||
</NUploadDragger>
|
||||
</NUpload>
|
||||
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.customImageAddress') }}</span>
|
||||
<NSwitch v-model:value="showWallpaperInput" />
|
||||
</div>
|
||||
<div v-if="showWallpaperInput" class="mt-1">
|
||||
<NInput v-model:value="panelState.panelConfig.backgroundImageSrc" type="text" size="small" clearable />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<span class="mr-[10px]">模糊</span>
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.vague') }}</span>
|
||||
<NSlider v-model:value="panelState.panelConfig.backgroundBlur" class="max-w-[200px]" :step="2" :max="20" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<span class="mr-[10px]">遮罩</span>
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.mask') }}</span>
|
||||
<NSlider v-model:value="panelState.panelConfig.backgroundMaskNumber" class="max-w-[200px]" :step="0.1" :max="1" />
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px] font-bold">
|
||||
内容区域
|
||||
{{ $t('apps.baseSettings.contentArea') }}
|
||||
</div>
|
||||
|
||||
<NGrid cols="2">
|
||||
<NGridItem span="12 400:12">
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<span class="mr-[10px]">最大宽度</span>
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.maxWidth') }}</span>
|
||||
<div class="flex">
|
||||
<NInputGroup>
|
||||
<NInput v-model:value="panelState.panelConfig.maxWidth" size="small" type="number" :maxlength="10" :style="{ width: '100px' }" placeholder="1200" />
|
||||
@@ -222,39 +250,51 @@ function resetPanelConfig() {
|
||||
</NGridItem>
|
||||
<NGridItem span="12 400:12">
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<span class="mr-[10px]">左右边距</span>
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.leftRightMargin') }}</span>
|
||||
<NSlider v-model:value="panelState.panelConfig.marginX" class="max-w-[200px]" :step="1" :max="100" />
|
||||
</div>
|
||||
</NGridItem>
|
||||
<NGridItem span="12 400:12">
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<span class="mr-[10px]">上边距 (%)</span>
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.topMargin') }} (%)</span>
|
||||
<NSlider v-model:value="panelState.panelConfig.marginTop" class="max-w-[200px]" :step="1" :max="50" />
|
||||
</div>
|
||||
</NGridItem>
|
||||
<NGridItem span="12 400:6">
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<span class="mr-[10px]">下边距 (%)</span>
|
||||
<span class="mr-[10px]">{{ $t('apps.baseSettings.bottomMargin') }} (%)</span>
|
||||
<NSlider v-model:value="panelState.panelConfig.marginBottom" class="max-w-[200px]" :step="1" :max="50" />
|
||||
</div>
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px] font-bold">
|
||||
{{ $t('apps.baseSettings.customFooter') }}
|
||||
</div>
|
||||
|
||||
<NInput
|
||||
v-model:value="panelState.panelConfig.footerHtml"
|
||||
type="textarea"
|
||||
clearable
|
||||
/>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<NPopconfirm
|
||||
@positive-click="resetPanelConfig"
|
||||
>
|
||||
<template #trigger>
|
||||
<NButton size="small" quaternary type="error">
|
||||
重置
|
||||
{{ $t('common.reset') }}
|
||||
</NButton>
|
||||
</template>
|
||||
确定要重置这些样式吗?
|
||||
{{ $t('apps.baseSettings.resetWarnText') }}
|
||||
</NPopconfirm>
|
||||
|
||||
<NButton size="small" quaternary type="success" class="ml-[10px]" @click="uploadCloud">
|
||||
立即保存
|
||||
{{ $t('common.save') }}
|
||||
</NButton>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormInst, FormRules } from 'naive-ui'
|
||||
import { NButton, NCard, NDivider, NForm, NFormItem, NInput, useDialog, useMessage } from 'naive-ui'
|
||||
import { NButton, NCard, NDivider, NForm, NFormItem, NInput, NSelect, useDialog, useMessage } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
import { useAuthStore, usePanelState, useUserStore } from '@/store'
|
||||
import { useAppStore, useAuthStore, usePanelState, useUserStore } from '@/store'
|
||||
import { languageOptions } from '@/utils/defaultData'
|
||||
import type { Language } from '@/store/modules/app/helper'
|
||||
import { logout } from '@/api'
|
||||
import { router } from '@/router'
|
||||
import { RoundCardModal, SvgIcon } from '@/components/common/'
|
||||
import { updateInfo, updatePassword } from '@/api/system/user'
|
||||
import { updateLocalUserInfo } from '@/utils/cmn'
|
||||
@@ -12,14 +13,21 @@ import { t } from '@/locales'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const authStore = useAuthStore()
|
||||
const appStore = useAppStore()
|
||||
const panelState = usePanelState()
|
||||
const ms = useMessage()
|
||||
const dialog = useDialog()
|
||||
|
||||
const languageValue = ref(appStore.language)
|
||||
// const themeValue = ref(appStore.theme)
|
||||
const nickName = ref(authStore.userInfo?.name || '')
|
||||
const isEditNickNameStatus = ref(false)
|
||||
const formRef = ref<FormInst | null>(null)
|
||||
|
||||
// const themeOptions: { label: string; key: string; value: Theme }[] = [
|
||||
// { label: t('apps.userInfo.themeStyle.dark'), key: 'dark', value: 'dark' },
|
||||
// { label: t('apps.userInfo.themeStyle.light'), key: 'light', value: 'light' },
|
||||
// { label: t('apps.userInfo.themeStyle.auto'), key: 'Auto', value: 'auto' },
|
||||
// ]
|
||||
const updatePasswordModalState = ref({
|
||||
show: false,
|
||||
loading: false,
|
||||
@@ -59,8 +67,9 @@ async function logoutApi() {
|
||||
userStore.resetUserInfo()
|
||||
authStore.removeToken()
|
||||
panelState.removeState()
|
||||
appStore.removeToken()
|
||||
ms.success(t('settingUserInfo.logoutSuccess'))
|
||||
router.push({ path: '/login' })
|
||||
// router.push({ path: '/login' })
|
||||
location.reload()// 强制刷新一下页面
|
||||
}
|
||||
|
||||
@@ -122,6 +131,18 @@ function handleLogout() {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function handleChangeLanuage(value: Language) {
|
||||
languageValue.value = value
|
||||
appStore.setLanguage(value)
|
||||
location.reload()
|
||||
}
|
||||
|
||||
// function handleChangeTheme(value: Theme) {
|
||||
// themeValue.value = value
|
||||
// appStore.setTheme(value)
|
||||
// // location.reload()
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -157,6 +178,24 @@ function handleLogout() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-[10px]">
|
||||
<div class="text-slate-500 font-bold">
|
||||
{{ $t('common.language') }}
|
||||
</div>
|
||||
<div class="max-w-[200px]">
|
||||
<NSelect v-model:value="languageValue" :options="languageOptions" @update-value="handleChangeLanuage" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="mt-[10px]">
|
||||
<div class="text-slate-500 font-bold">
|
||||
{{ $t('apps.userInfo.theme') }}
|
||||
</div>
|
||||
<div class="max-w-[200px]">
|
||||
<NSelect v-model:value="themeValue" :options="themeOptions" @update-value="handleChangeTheme" />
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<NDivider style="margin: 10px 0;" dashed />
|
||||
<div>
|
||||
<NButton size="small" text type="info" @click="updatePasswordModalState.show = !updatePasswordModalState.show">
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { PanelPanelConfigStyleEnum } from '@/enums'
|
||||
|
||||
interface Prop {
|
||||
cardTypeStyle: PanelPanelConfigStyleEnum
|
||||
class?: string
|
||||
backgroundColor?: string
|
||||
iconTextIconHideTitle?: boolean // 隐藏小图标标题
|
||||
iconTextColor?: string // 小图标文字颜色
|
||||
iconText?: string // 小图标文字
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Prop>(), {})
|
||||
|
||||
const defaultBackground = '#2a2a2a6b'
|
||||
const propClass = ref(props.class)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<!-- 详情图标 -->
|
||||
<div
|
||||
v-if="cardTypeStyle === PanelPanelConfigStyleEnum.info"
|
||||
class="w-full rounded-2xl transition-all duration-200 flex"
|
||||
:class="propClass"
|
||||
:style="{ backgroundColor: backgroundColor ?? defaultBackground }"
|
||||
>
|
||||
<slot name="info" />
|
||||
</div>
|
||||
|
||||
<!-- 极简图标(APP) -->
|
||||
<div
|
||||
v-if="cardTypeStyle === PanelPanelConfigStyleEnum.icon"
|
||||
>
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl sunpanel w-[70px] h-[70px] mx-auto transition-all duration-200"
|
||||
:class="propClass"
|
||||
:style="{ backgroundColor: backgroundColor ?? defaultBackground }"
|
||||
>
|
||||
<slot name="small" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!iconTextIconHideTitle"
|
||||
class="text-center app-icon-text-shadow cursor-pointer mt-[2px]"
|
||||
:style="{ color: iconTextColor }"
|
||||
>
|
||||
{{ iconText }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -8,6 +8,7 @@ import RoundCardModal from './RoundCardModal/index.vue'
|
||||
import SvgIconOnline from './SvgIconOnline/index.vue'
|
||||
import JsonImportExport from './JsonImportExport/index.vue'
|
||||
import AppLoader from './AppLoader/index.vue'
|
||||
import ItemCard from './ItemCard/index.vue'
|
||||
|
||||
export {
|
||||
Verification,
|
||||
@@ -20,4 +21,5 @@ export {
|
||||
SvgIconOnline,
|
||||
JsonImportExport,
|
||||
AppLoader,
|
||||
ItemCard,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user