mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-09 11:12:02 +00:00
feat(web): 升级至 v3.0 并重构组件与引导流程
- 提取应用引导逻辑至 useAppBootstrap 组合式函数 - 增强日期选择器组件,支持全类型与格式自动推导 - 修复资源泄漏:清理 RAF、定时器、事件与 mitt 监听 - 修复退出登录逻辑与异步导出流程 - 迁移 ::v-deep 至 Vue 3 标准 :deep 语法 - 移除调试日志并改进类型定义与注释文档
This commit is contained in:
+145
-113
@@ -1,3 +1,4 @@
|
||||
<!-- 缓存监控 -->
|
||||
<template>
|
||||
<div class="fa-full-height">
|
||||
<el-tabs>
|
||||
@@ -143,44 +144,46 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<ElTable :loading="loading" :data="cacheNames" row-key="cache_name">
|
||||
<template #empty>
|
||||
<ElEmpty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<ElTableColumn prop="cache_name" label="缓存名称" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<ElButton
|
||||
v-hasPerm="['module_monitor:cache:query']"
|
||||
type="primary"
|
||||
link
|
||||
@click="getCacheKeyList(row)"
|
||||
>
|
||||
{{ row.cache_name }}
|
||||
</ElButton>
|
||||
<div class="cache-table-wrap">
|
||||
<ElTable :loading="loading" :data="cacheNames" row-key="cache_name" height="100%">
|
||||
<template #empty>
|
||||
<ElEmpty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="remark" label="备注" show-overflow-tooltip />
|
||||
<ElTableColumn label="操作" width="60" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElPopconfirm
|
||||
class="box-item"
|
||||
:title="`确认删除缓存 ${row.cache_name} 吗?`"
|
||||
placement="top"
|
||||
@confirm="handleClearCacheName(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton
|
||||
v-hasPerm="['module_monitor:cache:delete']"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
icon="delete"
|
||||
/>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
<ElTableColumn prop="cache_name" label="缓存名称" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<ElButton
|
||||
v-hasPerm="['module_monitor:cache:query']"
|
||||
type="primary"
|
||||
link
|
||||
@click="getCacheKeyList(row)"
|
||||
>
|
||||
{{ row.cache_name }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="remark" label="备注" show-overflow-tooltip />
|
||||
<ElTableColumn label="操作" width="60" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElPopconfirm
|
||||
class="box-item"
|
||||
:title="`确认删除缓存 ${row.cache_name} 吗?`"
|
||||
placement="top"
|
||||
@confirm="handleClearCacheName(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton
|
||||
v-hasPerm="['module_monitor:cache:delete']"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
icon="delete"
|
||||
/>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</div>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
@@ -211,53 +214,56 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<ElTable
|
||||
:loading="subLoading"
|
||||
:data="cacheKeys.map((key) => ({ cacheKey: key }))"
|
||||
row-key="cacheKey"
|
||||
>
|
||||
<template #empty>
|
||||
<ElEmpty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<ElTableColumn prop="cacheKey" label="缓存键名" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<ElButton
|
||||
v-hasPerm="['module_monitor:cache:detail']"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleCacheValue(row.cacheKey)"
|
||||
>
|
||||
{{ row.cacheKey }}
|
||||
</ElButton>
|
||||
<div class="cache-table-wrap">
|
||||
<ElTable
|
||||
:loading="subLoading"
|
||||
:data="cacheKeys.map((key) => ({ cacheKey: key }))"
|
||||
row-key="cacheKey"
|
||||
height="100%"
|
||||
>
|
||||
<template #empty>
|
||||
<ElEmpty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="操作" width="60" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElPopconfirm
|
||||
class="box-item"
|
||||
:title="`确认删除键 ${row.cacheKey} 吗?`"
|
||||
placement="top"
|
||||
@confirm="handleClearCacheKey(row.cacheKey)"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton
|
||||
v-hasPerm="['module_monitor:cache:delete']"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
icon="delete"
|
||||
/>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
<ElTableColumn prop="cacheKey" label="缓存键名" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<ElButton
|
||||
v-hasPerm="['module_monitor:cache:detail']"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleCacheValue(row.cacheKey)"
|
||||
>
|
||||
{{ row.cacheKey }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="操作" width="60" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElPopconfirm
|
||||
class="box-item"
|
||||
:title="`确认删除键 ${row.cacheKey} 吗?`"
|
||||
placement="top"
|
||||
@confirm="handleClearCacheKey(row.cacheKey)"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton
|
||||
v-hasPerm="['module_monitor:cache:delete']"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
icon="delete"
|
||||
/>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</div>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
<!-- 缓存内容 -->
|
||||
<ElCol :span="8">
|
||||
<ElCard :loading="loading" shadow="hover">
|
||||
<ElCol :span="8" class="cache-mgmt-col">
|
||||
<ElCard :loading="loading" shadow="hover" class="cache-mgmt-card">
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -284,23 +290,29 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<ElForm :model="cacheForm" label-suffix=":" label-width="auto" label-position="top">
|
||||
<ElFormItem label="缓存名称">
|
||||
<ElInput v-model="cacheForm.cache_name" readonly placeholder="缓存名称" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="缓存键名">
|
||||
<ElInput v-model="cacheForm.cache_key" readonly placeholder="缓存键名" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="缓存内容">
|
||||
<ElInput
|
||||
v-model="cacheForm.cache_value"
|
||||
type="textarea"
|
||||
:rows="20"
|
||||
readonly
|
||||
placeholder="缓存内容"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<div class="cache-form-wrap">
|
||||
<ElForm
|
||||
:model="cacheForm"
|
||||
label-suffix=":"
|
||||
label-width="auto"
|
||||
label-position="top"
|
||||
>
|
||||
<ElFormItem label="缓存名称">
|
||||
<ElInput v-model="cacheForm.cache_name" readonly placeholder="缓存名称" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="缓存键名">
|
||||
<ElInput v-model="cacheForm.cache_key" readonly placeholder="缓存键名" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="缓存内容" class="cache-value-item">
|
||||
<ElInput
|
||||
v-model="cacheForm.cache_value"
|
||||
type="textarea"
|
||||
readonly
|
||||
placeholder="缓存内容"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</div>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
@@ -432,22 +444,19 @@ async function handleCacheValue(cacheKey: string) {
|
||||
|
||||
// 清理全部缓存
|
||||
const handleClearCacheAll = async () => {
|
||||
ElMessageBox.confirm("确定要清理全部缓存吗?", "危险!", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
return await CacheAPI.deleteCacheAll();
|
||||
})
|
||||
.then(() => {
|
||||
getCacheNameList();
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
if (error !== "cancel") {
|
||||
console.error("清理全部缓存失败:", error);
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm("确定要清理全部缓存吗?", "危险!", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
});
|
||||
await CacheAPI.deleteCacheAll();
|
||||
getCacheNameList();
|
||||
} catch (error: unknown) {
|
||||
if (error !== "cancel") {
|
||||
console.error("清理全部缓存失败:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 监控数据获取
|
||||
@@ -567,7 +576,7 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
|
||||
::v-deep(.el-card__body) {
|
||||
:deep(.el-card__body) {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
@@ -607,7 +616,30 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
|
||||
::v-deep(.el-card__body) {
|
||||
:deep(.el-card__body) {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
|
||||
> .cache-table-wrap,
|
||||
> .cache-form-wrap {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cache-table-wrap {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cache-form-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
.el-form {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -125,21 +125,20 @@ const onlineSearchItems = computed<SearchFormItem[]>(() => [
|
||||
const clearAllLoading = ref(false);
|
||||
|
||||
function kickSession(sessionId: string) {
|
||||
ElMessageBox.confirm(`确认强制退出会话 ${sessionId}?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
await OnlineAPI.deleteOnline(sessionId);
|
||||
ElMessage.success("操作成功");
|
||||
await refreshData();
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
(async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确认强制退出会话 ${sessionId}?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
});
|
||||
await OnlineAPI.deleteOnline(sessionId);
|
||||
ElMessage.success("操作成功");
|
||||
await refreshData();
|
||||
} catch {
|
||||
// 用户取消或操作失败
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -259,24 +258,23 @@ async function onResetSearch() {
|
||||
}
|
||||
|
||||
function handleClearAll() {
|
||||
ElMessageBox.confirm("确认强制退出所有用户?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
clearAllLoading.value = true;
|
||||
await OnlineAPI.clearOnline();
|
||||
ElMessage.success("操作成功");
|
||||
await refreshData();
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
clearAllLoading.value = false;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
(async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm("确认强制退出所有用户?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
});
|
||||
clearAllLoading.value = true;
|
||||
await OnlineAPI.clearOnline();
|
||||
ElMessage.success("操作成功");
|
||||
await refreshData();
|
||||
} catch {
|
||||
// 用户取消
|
||||
} finally {
|
||||
clearAllLoading.value = false;
|
||||
}
|
||||
})();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
</FaTableHeader>
|
||||
|
||||
<!-- 面包屑与表头已占用高度:表格高度须在独立 flex 子项内计算,否则分页会被挤出卡片 -->
|
||||
<div class="resource-table-region min-h-0 flex flex-1 flex-col overflow-hidden pb-3">
|
||||
<div class="resource-table-region min-h-0 flex flex-1 flex-col overflow-hidden">
|
||||
<FaTable
|
||||
row-key="file_url"
|
||||
:show-table-header="false"
|
||||
@@ -633,14 +633,14 @@ async function handleBatchDelete() {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.resource-monitor-page ::v-deep(.resource-monitor-card.el-card > .el-card__body) {
|
||||
.resource-monitor-page :deep(.resource-monitor-card.el-card > .el-card__body) {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
::v-deep(.el-breadcrumb__item) {
|
||||
:deep(.el-breadcrumb__item) {
|
||||
&.is-link {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
|
||||
Reference in New Issue
Block a user