refactor: 前端项目代码优化与样式调整

本次提交包含多项改进:
1. 新增移动端断点常量并全局复用,重构响应式设备检测逻辑
2. 优化表格卡片样式与布局,统一移除多余的shadow属性
3. 调整表单、按钮、分页组件的移动端适配样式
4. 补充邮箱国际化文案与图表组件标题属性
5. 优化菜单hover效果、全局搜索样式与深色主题变量
6. 修复权限配置、组件默认值与布局排版问题
7. 简化部分代码冗余逻辑,统一代码风格
This commit is contained in:
zhangtao
2026-06-28 20:50:54 +08:00
parent 50c0493eb2
commit a0c7091697
90 changed files with 2062 additions and 1921 deletions
+11 -7
View File
@@ -1,5 +1,5 @@
<template>
<div class="fa-full-height">
<div class="flex flex-col relative last:mb-0">
<ElTabs>
<!-- 监控信息 Tab -->
<ElTabPane label="监控信息">
@@ -13,7 +13,7 @@
<span class="font-medium">Redis监控信息</span>
</div>
</template>
<FaDescriptions :column="6" :scrollbar="false">
<FaDescriptions :column="descColumns" :scrollbar="false">
<ElDescriptionsItem label="Redis版本">
{{ cache.info?.redis_version || "-" }}
</ElDescriptionsItem>
@@ -61,7 +61,7 @@
</ElRow>
<ElRow :gutter="16" class="flex-1 min-h-0">
<ElCol :span="12" class="flex flex-col min-h-0">
<ElCol :xs="24" :sm="12" class="mb-5">
<ElCard shadow="hover" class="flex-1 flex flex-col chart-card">
<template #header>
<div class="flex items-center gap-2">
@@ -72,7 +72,7 @@
<div ref="commandstats" class="chart-container" />
</ElCard>
</ElCol>
<ElCol :span="12" class="flex flex-col min-h-0">
<ElCol :xs="24" :sm="12" class="mb-5">
<ElCard shadow="hover" class="flex-1 flex flex-col chart-card">
<template #header>
<div class="flex items-center gap-2">
@@ -92,7 +92,7 @@
<div class="cache-mgmt-tab">
<ElRow :gutter="16" class="flex-1 min-h-0">
<!-- 缓存列表 -->
<ElCol :span="8" class="cache-mgmt-col">
<ElCol :xs="24" :md="8" class="cache-mgmt-col mb-5 h-full">
<ElCard :loading="loading" shadow="hover" class="cache-mgmt-card">
<template #header>
<div class="flex items-center justify-between">
@@ -152,7 +152,7 @@
</ElCol>
<!-- 键名列表 -->
<ElCol :span="8" class="cache-mgmt-col">
<ElCol :xs="24" :md="8" class="cache-mgmt-col mb-5 h-full">
<ElCard :loading="loading" shadow="hover" class="cache-mgmt-card">
<template #header>
<div class="flex items-center justify-between">
@@ -215,7 +215,7 @@
</ElCol>
<!-- 缓存内容 -->
<ElCol :span="8" class="cache-mgmt-col">
<ElCol :xs="24" :md="8" class="cache-mgmt-col mb-5 h-full">
<ElCard :loading="loading" shadow="hover" class="cache-mgmt-card">
<template #header>
<div class="flex items-center justify-between">
@@ -270,9 +270,13 @@ import CacheAPI, {
type RedisInfo,
} from "@/api/module_monitor/cache";
import * as echarts from "echarts";
import { useWindowSize } from "@vueuse/core";
defineOptions({ name: "CacheMonitor" });
const { width: winWidth } = useWindowSize();
const descColumns = computed(() => (winWidth.value < 768 ? 2 : 6));
const cacheNames = ref<CacheInfo[]>([]);
const cacheKeys = ref<string[]>([]);
const loading = ref(true);
@@ -17,11 +17,7 @@
@reset="onResetSearch"
/>
<ElCard
shadow="hover"
class="fa-table-card"
:style="{ 'margin-top': showSearchBar ? '12px' : '0' }"
>
<ElCard class="fa-table-card" :style="{ 'margin-top': showSearchBar ? '12px' : '0' }">
<FaTableHeader
v-model:columns="columnChecks"
v-model:showSearchBar="showSearchBar"
@@ -64,12 +60,12 @@ import { h, ref, computed } from "vue";
import { useTable } from "@/hooks/core/useTable";
import OnlineAPI, { type OnlineUserTable } from "@/api/module_monitor/online";
import type { ColumnOption } from "@/types/component";
import { ElMessageBox, ElTooltip } from "element-plus";
import { ElMessageBox } from "element-plus";
import { useAuth } from "@/hooks/core/useAuth";
import { renderTableOperationCell, type TableOperationAction } from "@/utils/table";
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
import type FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
import FaCopyButton from "@/components/others/fa-copy-button/index.vue";
import FaButtonTable from "@/components/forms/fa-button-table/index.vue";
const { hasAuth } = useAuth();
@@ -143,6 +139,23 @@ function kickSession(sessionId: string) {
})();
}
function buildOnlineRowActions(row: OnlineUserTable): TableOperationAction[] {
const all: TableOperationAction[] = [
{
key: "kick",
label: "强退",
artType: "delete",
perm: "module_monitor:online:delete",
run: () => kickSession(row.session_id),
},
];
return all.filter((a) => a.perm != null && hasAuth(a.perm));
}
function formatOnlineOperationCell(row: OnlineUserTable) {
return renderTableOperationCell(buildOnlineRowActions(row));
}
const {
columns,
columnChecks,
@@ -226,20 +239,8 @@ const {
label: "操作",
width: 88,
fixed: "right",
align: "right",
formatter: (row: OnlineUserTable) => {
if (!hasAuth("module_monitor:online:delete")) {
return h("span", { class: "text-g-400" }, "—");
}
return h(ElTooltip, { content: "强退", placement: "top" }, () =>
h("span", { class: "inline-flex" }, [
h(FaButtonTable, {
type: "delete",
onClick: () => kickSession(row.session_id),
}),
])
);
},
align: "center",
formatter: (row: OnlineUserTable) => formatOnlineOperationCell(row),
},
],
},
@@ -223,7 +223,7 @@ import type { ColumnOption } from "@/types/component";
import { useAuth } from "@/hooks/core/useAuth";
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
import type FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
import FaButtonTable from "@/components/forms/fa-button-table/index.vue";
import { renderTableOperationCell, type TableOperationAction } from "@/utils/table";
const { hasAuth } = useAuth();
@@ -391,53 +391,51 @@ const {
{
prop: "operation",
label: "操作",
width: 220,
width: 136,
fixed: "right",
align: "center",
formatter: (row: ResourceItem) =>
h(
ElSpace,
{ wrap: true, size: 4 },
() =>
[
!row.is_dir && hasAuth("module_monitor:resource:download")
? h(ElTooltip, { content: "下载", placement: "top" }, () =>
h("span", { class: "inline-flex" }, [
h(FaButtonTable, {
type: "view",
icon: "ri:download-line",
onClick: () => void handleDownload(row),
}),
])
)
: null,
hasAuth("module_monitor:resource:rename")
? h(ElTooltip, { content: "重命名", placement: "top" }, () =>
h("span", { class: "inline-flex" }, [
h(FaButtonTable, {
type: "edit",
onClick: () => void handleRenameOpen(row),
}),
])
)
: null,
hasAuth("module_monitor:resource:delete")
? h(ElTooltip, { content: "删除", placement: "top" }, () =>
h("span", { class: "inline-flex" }, [
h(FaButtonTable, {
type: "delete",
onClick: () => void handleDelete(row),
}),
])
)
: null,
].filter(Boolean) as ReturnType<typeof h>[]
),
formatter: (row: ResourceItem) => formatResourceOperationCell(row),
},
],
},
});
function buildResourceRowActions(row: ResourceItem): TableOperationAction[] {
const actions: TableOperationAction[] = [];
if (!row.is_dir && hasAuth("module_monitor:resource:download")) {
actions.push({
key: "download",
label: "下载",
artType: "view",
icon: "ri:download-line",
run: () => void handleDownload(row),
});
}
if (hasAuth("module_monitor:resource:rename")) {
actions.push({
key: "rename",
label: "重命名",
artType: "edit",
run: () => void handleRenameOpen(row),
});
}
if (hasAuth("module_monitor:resource:delete")) {
actions.push({
key: "delete",
label: "删除",
artType: "delete",
run: () => void handleDelete(row),
});
}
return actions;
}
function formatResourceOperationCell(row: ResourceItem) {
return renderTableOperationCell(buildResourceRowActions(row), {
wrapperClass: "inline-flex items-center justify-center gap-1",
});
}
function handleBreadcrumbClick(item: { path: string }) {
currentPath.value = item.path;
updateBreadcrumb();
@@ -1,9 +1,9 @@
<template>
<div class="server-container">
<ElRow :gutter="16" class="server-row">
<div class="flex flex-col relative last:mb-0">
<ElRow :gutter="16">
<!-- CPU 使用情况 -->
<ElCol :span="12" class="server-col">
<ElCard :loading="loading" shadow="hover" class="server-card">
<ElCol :xs="24" :sm="12" class="mb-5">
<ElCard :loading="loading" shadow="hover">
<template #header>
<div class="flex items-center gap-2">
<FaSvgIcon icon="ri:cpu-line" class="text-lg" />
@@ -11,7 +11,7 @@
</div>
</template>
<div class="flex items-center flex-col gap-4">
<div class="flex items-center gap-6">
<div class="flex items-center gap-6 max-md:flex-col max-md:gap-3">
<ElProgress
type="circle"
:percentage="server.cpu?.used || 0"
@@ -45,8 +45,8 @@
</ElCol>
<!-- 内存使用情况 -->
<ElCol :span="12" class="server-col">
<ElCard :loading="loading" shadow="hover" class="server-card">
<ElCol :xs="24" :sm="12" class="mb-5">
<ElCard :loading="loading" shadow="hover">
<template #header>
<div class="flex items-center gap-2">
<FaSvgIcon icon="ri:ram-line" class="text-lg" />
@@ -54,7 +54,7 @@
</div>
</template>
<div class="flex items-center flex-col gap-4">
<div class="flex items-center gap-6">
<div class="flex items-center gap-6 max-md:flex-col max-md:gap-3">
<ElProgress
type="circle"
:percentage="server.mem?.usage || 0"
@@ -88,10 +88,10 @@
</ElCol>
</ElRow>
<ElRow :gutter="16" class="server-row">
<ElRow :gutter="16">
<!-- 服务器基本信息 -->
<ElCol :span="12" class="server-col">
<ElCard :loading="loading" shadow="hover" class="server-card">
<ElCol :xs="24" :sm="12" class="mb-5">
<ElCard :loading="loading" shadow="hover">
<template #header>
<div class="flex items-center gap-2">
<FaSvgIcon icon="ri:server-line" class="text-lg" />
@@ -116,8 +116,8 @@
</ElCol>
<!-- Python运行环境 -->
<ElCol :span="12" class="server-col">
<ElCard :loading="loading" shadow="hover" class="server-card">
<ElCol :xs="24" :sm="12" class="mb-5">
<ElCard :loading="loading" shadow="hover">
<template #header>
<div class="flex items-center gap-2">
<FaSvgIcon icon="ri:code-s-slash-line" class="text-lg" />
@@ -149,9 +149,9 @@
</ElRow>
<!-- 磁盘使用情况 -->
<ElRow :gutter="16" class="server-row">
<ElCol :span="24" class="server-col">
<ElCard :loading="loading" shadow="hover" class="server-card">
<ElRow :gutter="16">
<ElCol :span="24" class="mb-5">
<ElCard :loading="loading" shadow="hover">
<template #header>
<div class="flex items-center gap-2">
<FaSvgIcon icon="ri:hard-drive-2-line" class="text-lg" />
@@ -227,34 +227,9 @@ onMounted(() => {
</script>
<style scoped lang="scss">
// 与 dashboard 首页一致:自定义圆角 + 边框色
:deep(.el-card) {
--el-card-border-radius: calc(var(--custom-radius) + 2px);
border: 1px solid var(--fa-card-border);
}
.server-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.server-row {
.server-col {
display: flex;
flex-direction: column;
min-height: 0;
.server-card {
display: flex;
flex: 1;
flex-direction: column;
:deep(.el-card__body) {
flex: 1;
}
}
}
}
</style>