feat: 添加高亮行与序号列,优化全屏及缓存布局

This commit is contained in:
zhangtao
2026-05-14 00:10:14 +08:00
parent 17356ce582
commit 86bc5d35fe
35 changed files with 244 additions and 109 deletions
+1 -22
View File
@@ -2682,32 +2682,11 @@
], ],
"client": "pc" "client": "pc"
}, },
{
"name": "会话聊天",
"type": 2,
"icon": "ri:message-3-line",
"order": 2,
"permission": "module_ai:chat:query",
"route_name": "FaChat",
"route_path": "/ai/fachat",
"component_path": "module_ai/fachat/index",
"status": "0",
"keep_alive": true,
"hidden": false,
"always_show": false,
"title": "会话聊天",
"params": null,
"affix": false,
"redirect": null,
"description": "会话聊天(FaChat)",
"children": [],
"client": "pc"
},
{ {
"name": "会话记忆", "name": "会话记忆",
"type": 2, "type": 2,
"icon": "ri:chat-3-line", "icon": "ri:chat-3-line",
"order": 3, "order": 2,
"permission": "module_ai:chat:query", "permission": "module_ai:chat:query",
"route_name": "Memory", "route_name": "Memory",
"route_path": "/ai/memory", "route_path": "/ai/memory",
@@ -413,6 +413,7 @@ defineExpose({
<ElButton type="danger" @click="handleClear" v-ripple>清除数据</ElButton> <ElButton type="danger" @click="handleClear" v-ripple>清除数据</ElButton>
<FaTable :data="tableData" style="margin-top: 10px"> <FaTable :data="tableData" style="margin-top: 10px">
<ElTableColumn type="index" label="序号" width="60" />
<ElTableColumn <ElTableColumn
v-for="key in Object.keys(headers)" v-for="key in Object.keys(headers)"
:key="key" :key="key"
@@ -86,6 +86,7 @@ const handleFileChange = async (uploadFile: UploadFile) => {
<ElButton type="danger" @click="handleClear" v-ripple>清除数据</ElButton> <ElButton type="danger" @click="handleClear" v-ripple>清除数据</ElButton>
<FaTable :data="tableData" style="margin-top: 10px"> <FaTable :data="tableData" style="margin-top: 10px">
<ElTableColumn type="index" label="序号" width="60" />
<ElTableColumn <ElTableColumn
v-for="key in Object.keys(headers)" v-for="key in Object.keys(headers)"
:key="key" :key="key"
@@ -59,6 +59,61 @@
</div> </div>
</div> </div>
</ElPopover> </ElPopover>
<!-- 礼花效果介绍弹窗(放在 Popover 外,避免被 Popover 销毁) -->
<ElDialog
v-model="fireworksDialogVisible"
title="礼花效果"
width="480px"
:close-on-click-modal="true"
>
<div class="px-2">
<div class="mb-5 flex items-start gap-4">
<div class="flex-cc size-14 rounded-xl bg-gradient-to-br from-purple-400 to-indigo-500">
<FaSvgIcon icon="ri:loader-line" class="text-2xl text-white" />
</div>
<div class="flex-1">
<h4 class="m-0 text-base font-semibold">节日礼花动画</h4>
<p class="mt-1 text-sm text-g-500 leading-relaxed">
根据当前节日自动匹配对应素材的全屏 Canvas 烟花效果,支持多种粒子形状和绚丽色彩。
</p>
</div>
</div>
<ElDivider />
<div class="space-y-3">
<div class="flex items-center gap-3">
<ElTag type="primary" class="!rounded-md !border-0 flex-shrink-0">快捷键</ElTag>
<span class="text-sm text-g-600">
<ElTag size="small" class="!mr-1">Ctrl</ElTag>
+
<ElTag size="small" class="!mx-1">Shift</ElTag>
+
<ElTag size="small" class="!ml-1">P</ElTag>
<span class="mx-2 text-g-400">/</span>
<ElTag size="small" class="!mr-1">⌘</ElTag>
+
<ElTag size="small" class="!mx-1">Shift</ElTag>
+
<ElTag size="small" class="!ml-1">P</ElTag>
</span>
</div>
<div class="flex items-center gap-3">
<ElTag type="success" class="!rounded-md !border-0 flex-shrink-0">节日素材</ElTag>
<span class="text-sm text-g-600">
礼花素材与当前节日配置一致,自动匹配春节、元宵、中秋等传统节日主题
</span>
</div>
<div class="flex items-center gap-3">
<ElTag type="warning" class="!rounded-md !border-0 flex-shrink-0">触发方式</ElTag>
<span class="text-sm text-g-600">
页面加载时自动检测节日并连发,也可通过快捷键手动触发一次
</span>
</div>
</div>
</div>
</ElDialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -69,6 +124,7 @@ defineOptions({ name: "FaFastEnter" });
const router = useRouter(); const router = useRouter();
const popoverRef = ref(); const popoverRef = ref();
const fireworksDialogVisible = ref(false);
// 使用快速入口配置 // 使用快速入口配置
const { enabledApplications, enabledQuickLinks } = useFastEnter(); const { enabledApplications, enabledQuickLinks } = useFastEnter();
@@ -104,6 +160,11 @@ const handleNavigate = (
* @param application 应用配置对象 * @param application 应用配置对象
*/ */
const handleApplicationClick = (application: FastEnterApplication): void => { const handleApplicationClick = (application: FastEnterApplication): void => {
if (application.isDialog) {
popoverRef.value?.hide();
fireworksDialogVisible.value = true;
return;
}
handleNavigate(application.routeName, application.link, application.routeQuery); handleNavigate(application.routeName, application.link, application.routeQuery);
}; };
@@ -112,6 +173,11 @@ const handleApplicationClick = (application: FastEnterApplication): void => {
* @param quickLink 快速链接配置对象 * @param quickLink 快速链接配置对象
*/ */
const handleQuickLinkClick = (quickLink: FastEnterQuickLink): void => { const handleQuickLinkClick = (quickLink: FastEnterQuickLink): void => {
if (quickLink.isDialog) {
popoverRef.value?.hide();
fireworksDialogVisible.value = true;
return;
}
handleNavigate(quickLink.routeName, quickLink.link, quickLink.routeQuery); handleNavigate(quickLink.routeName, quickLink.link, quickLink.routeQuery);
}; };
</script> </script>
@@ -139,6 +139,9 @@
<ElCheckbox v-model="isHeaderBackground" :value="true"> <ElCheckbox v-model="isHeaderBackground" :value="true">
{{ t("table.headerBackground") }} {{ t("table.headerBackground") }}
</ElCheckbox> </ElCheckbox>
<ElCheckbox v-model="highlightCurrentRow" :value="true">
{{ t("table.highlightCurrentRow") }}
</ElCheckbox>
</div> </div>
</ElPopover> </ElPopover>
<slot name="right"></slot> <slot name="right"></slot>
@@ -179,7 +182,7 @@ interface Props {
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
fullClass: "fa-page-view", fullClass: "fa-table-card",
layout: "search,refresh,size,fullscreen,columns,rowDrag,settings", layout: "search,refresh,size,fullscreen,columns,rowDrag,settings",
showSearchBar: undefined, showSearchBar: undefined,
}); });
@@ -224,7 +227,8 @@ const tableSizeOptions = [
]; ];
const tableStore = useTableStore(); const tableStore = useTableStore();
const { tableSize, isZebra, isBorder, isHeaderBackground, isRowDrag } = storeToRefs(tableStore); const { tableSize, isZebra, isBorder, isHeaderBackground, isRowDrag, highlightCurrentRow } =
storeToRefs(tableStore);
const toggleRowDrag = () => { const toggleRowDrag = () => {
tableStore.setIsRowDrag(!isRowDrag.value); tableStore.setIsRowDrag(!isRowDrag.value);
@@ -289,12 +293,29 @@ const isFullScreen = ref(false);
/** 保存原始的 overflow 样式,用于退出全屏时恢复 */ /** 保存原始的 overflow 样式,用于退出全屏时恢复 */
const originalOverflow = ref(""); const originalOverflow = ref("");
/**
* 查找可全屏的容器元素
* 优先取 .fa-table-card 的父级(包含搜索栏 + 表格卡片),其次降级到 fullClass
*/
const findFullscreenContainer = (): HTMLElement | null => {
const headerEl = document.getElementById("fa-table-header");
if (headerEl) {
const card = headerEl.closest(`.${props.fullClass}`);
if (card?.parentElement) {
// 取父级 — 通常包裹了搜索栏 + 表格卡片
return card.parentElement as HTMLElement;
}
if (card) return card as HTMLElement;
}
return document.querySelector(`.${props.fullClass}`);
};
/** /**
* 切换全屏状态 * 切换全屏状态
* 进入全屏时会隐藏页面滚动条,退出时恢复原状态 * 进入全屏时会隐藏页面滚动条,退出时恢复原状态
*/ */
const toggleFullScreen = () => { const toggleFullScreen = () => {
const el = document.querySelector(`.${props.fullClass}`); const el = findFullscreenContainer();
if (!el) return; if (!el) return;
isFullScreen.value = !isFullScreen.value; isFullScreen.value = !isFullScreen.value;
@@ -336,7 +357,7 @@ onUnmounted(() => {
// 如果组件在全屏状态下被卸载,恢复页面滚动状态 // 如果组件在全屏状态下被卸载,恢复页面滚动状态
if (isFullScreen.value) { if (isFullScreen.value) {
document.body.style.overflow = originalOverflow.value; document.body.style.overflow = originalOverflow.value;
const el = document.querySelector(`.${props.fullClass}`); const el = findFullscreenContainer();
if (el) { if (el) {
el.classList.remove("el-full-screen"); el.classList.remove("el-full-screen");
} }
@@ -116,8 +116,15 @@ const elTableRef = ref<InstanceType<typeof ElTable> | null>(null);
const paginationRef = ref<HTMLElement>(); const paginationRef = ref<HTMLElement>();
const tableHeaderRef = ref<HTMLElement>(); const tableHeaderRef = ref<HTMLElement>();
const tableStore = useTableStore(); const tableStore = useTableStore();
const { isBorder, isZebra, tableSize, isFullScreen, isHeaderBackground, isRowDrag } = const {
storeToRefs(tableStore); isBorder,
isZebra,
tableSize,
isFullScreen,
isHeaderBackground,
isRowDrag,
highlightCurrentRow,
} = storeToRefs(tableStore);
/** 分页配置接口 */ /** 分页配置接口 */
interface PaginationConfig { interface PaginationConfig {
@@ -306,6 +313,7 @@ const mergedTableProps = computed(() => ({
border: border.value, border: border.value,
size: size.value, size: size.value,
headerCellStyle: headerCellStyle.value, headerCellStyle: headerCellStyle.value,
highlightCurrentRow: highlightCurrentRow.value,
// Element Plus 默认值为 true,未显式传入时不应被 FaTable 覆盖成 false。 // Element Plus 默认值为 true,未显式传入时不应被 FaTable 覆盖成 false。
selectOnIndeterminate: hasExplicitTableProp("selectOnIndeterminate") selectOnIndeterminate: hasExplicitTableProp("selectOnIndeterminate")
? props.selectOnIndeterminate ? props.selectOnIndeterminate
+10 -10
View File
@@ -29,13 +29,13 @@ const fastEnterConfig: FastEnterConfig = {
routeName: "DashboardAnalysis", routeName: "DashboardAnalysis",
}, },
{ {
name: "礼花效果", name: "定价",
description: "动画特效展示", description: "价格方案与套餐选择",
icon: "ri:loader-line", icon: "ri:money-cny-box-line",
iconColor: "#7A7FFF", iconColor: "#FF6B35",
enabled: true, enabled: true,
order: 3, order: 3,
routeName: "Fireworks", routeName: "FastlinkPricing",
}, },
{ {
name: "聊天", name: "聊天",
@@ -44,7 +44,7 @@ const fastEnterConfig: FastEnterConfig = {
iconColor: "#13DEB9", iconColor: "#13DEB9",
enabled: true, enabled: true,
order: 4, order: 4,
routeName: "Chat", routeName: "FastlinkFachat",
}, },
{ {
name: "官方文档", name: "官方文档",
@@ -77,7 +77,7 @@ const fastEnterConfig: FastEnterConfig = {
name: "操作手册", name: "操作手册",
description: "产品操作指南", description: "产品操作指南",
icon: "ri:book-2-line", icon: "ri:book-2-line",
iconColor: "#38C0FC", iconColor: "#009688",
enabled: true, enabled: true,
order: 8, order: 8,
routeName: "FastlinkTutorial", routeName: "FastlinkTutorial",
@@ -104,16 +104,16 @@ const fastEnterConfig: FastEnterConfig = {
routeName: "Login", routeName: "Login",
}, },
{ {
name: "定价", name: "礼花效果",
enabled: true, enabled: true,
order: 4, order: 4,
routeName: "FastlinkPricing", isDialog: true,
}, },
{ {
name: "个人中心", name: "个人中心",
enabled: true, enabled: true,
order: 5, order: 5,
routeName: "Profile", routeName: "FastlinkProfile",
}, },
{ {
name: "留言管理", name: "留言管理",
+4
View File
@@ -540,6 +540,9 @@
"workplace": { "workplace": {
"title": "Workbench" "title": "Workbench"
}, },
"fachat": {
"title": "Fachat"
},
"changelog": { "changelog": {
"title": "Release notes" "title": "Release notes"
}, },
@@ -620,6 +623,7 @@
"zebra": "Zebra", "zebra": "Zebra",
"border": "Border", "border": "Border",
"headerBackground": "Header BG", "headerBackground": "Header BG",
"highlightCurrentRow": "Highlight Row",
"toolbar": { "toolbar": {
"hideSearch": "Hide search area", "hideSearch": "Hide search area",
"showSearch": "Show search area", "showSearch": "Show search area",
+4
View File
@@ -540,6 +540,9 @@
"workplace": { "workplace": {
"title": "工作台" "title": "工作台"
}, },
"fachat": {
"title": "聊天"
},
"changelog": { "changelog": {
"title": "更新日志" "title": "更新日志"
}, },
@@ -620,6 +623,7 @@
"zebra": "斑马纹", "zebra": "斑马纹",
"border": "边框", "border": "边框",
"headerBackground": "表头背景", "headerBackground": "表头背景",
"highlightCurrentRow": "高亮行",
"toolbar": { "toolbar": {
"hideSearch": "隐藏搜索区域", "hideSearch": "隐藏搜索区域",
"showSearch": "显示搜索区域", "showSearch": "显示搜索区域",
+17 -5
View File
@@ -11,6 +11,7 @@ import type { AppRouteRecord, RouteMeta } from "@/types/router";
import { defineComponent, h, onMounted, ref } from "vue"; import { defineComponent, h, onMounted, ref } from "vue";
import type { RouteRecordRaw } from "vue-router"; import type { RouteRecordRaw } from "vue-router";
import { RouterView, useRoute } from "vue-router"; import { RouterView, useRoute } from "vue-router";
import { t } from "@wangeditor-next/editor";
/** 首页 / 仪表盘父级 meta(侧栏、静态子路由共用) */ /** 首页 / 仪表盘父级 meta(侧栏、静态子路由共用) */
export const HOME_MENU_META: RouteMeta = { export const HOME_MENU_META: RouteMeta = {
@@ -376,14 +377,14 @@ export const staticRoutes: AppRouteRecordRaw[] = [
{ {
path: "profile", path: "profile",
name: "FastlinkProfile", name: "FastlinkProfile",
meta: { title: "个人中心", icon: "ri:user-line", hidden: true }, meta: { title: t("menus.system.userCenter"), icon: "ri:user-line", hidden: true },
component: () => import("@views/fastlink/current/profile.vue"), component: () => import("@views/fastlink/current/profile.vue"),
}, },
{ {
path: "changelog", path: "changelog",
name: "FastlinkChangeLog", name: "FastlinkChangeLog",
meta: { meta: {
title: "menus.changelog.title", title: t("menus.changelog.title"),
icon: "ri:draft-line", icon: "ri:draft-line",
hidden: true, hidden: true,
keepAlive: true, keepAlive: true,
@@ -394,7 +395,7 @@ export const staticRoutes: AppRouteRecordRaw[] = [
path: "pricing", path: "pricing",
name: "FastlinkPricing", name: "FastlinkPricing",
meta: { meta: {
title: "menus.dashboard.pricing", title: t("menus.dashboard.pricing"),
icon: "ri:money-cny-box-line", icon: "ri:money-cny-box-line",
hidden: true, hidden: true,
keepAlive: true, keepAlive: true,
@@ -405,7 +406,7 @@ export const staticRoutes: AppRouteRecordRaw[] = [
path: "article/list", path: "article/list",
name: "FastlinkArticleList", name: "FastlinkArticleList",
meta: { meta: {
title: "menus.article.articleList", title: t("menus.article.articleList"),
icon: "ri:article-line", icon: "ri:article-line",
hidden: true, hidden: true,
keepAlive: true, keepAlive: true,
@@ -416,13 +417,24 @@ export const staticRoutes: AppRouteRecordRaw[] = [
path: "tutorial", path: "tutorial",
name: "FastlinkTutorial", name: "FastlinkTutorial",
meta: { meta: {
title: "menus.dashboard.tutorial", title: t("menus.dashboard.tutorial"),
icon: "ri:book-2-line", icon: "ri:book-2-line",
hidden: true, hidden: true,
keepAlive: true, keepAlive: true,
}, },
component: () => import("@views/fastlink/tutorial/index.vue"), component: () => import("@views/fastlink/tutorial/index.vue"),
}, },
{
path: "fachat",
name: "FastlinkFachat",
meta: {
title: t("menus.fachat"),
icon: "ri:message-3-line",
hidden: true,
keepAlive: true,
},
component: () => import("@views/fastlink/fachat/index.vue"),
},
], ],
}, },
], ],
@@ -24,6 +24,8 @@ export const useTableStore = defineStore(
const isFullScreen = ref(false); const isFullScreen = ref(false);
/** 工具栏「行拖拽」;仅当表格数据为可变数组时有效 */ /** 工具栏「行拖拽」;仅当表格数据为可变数组时有效 */
const isRowDrag = ref(false); const isRowDrag = ref(false);
/** 高亮当前行 */
const highlightCurrentRow = ref(false);
const setTableSize = (size: TableSizeEnum) => (tableSize.value = size); const setTableSize = (size: TableSizeEnum) => (tableSize.value = size);
const setIsZebra = (value: boolean) => (isZebra.value = value); const setIsZebra = (value: boolean) => (isZebra.value = value);
@@ -31,6 +33,7 @@ export const useTableStore = defineStore(
const setIsHeaderBackground = (value: boolean) => (isHeaderBackground.value = value); const setIsHeaderBackground = (value: boolean) => (isHeaderBackground.value = value);
const setIsFullScreen = (value: boolean) => (isFullScreen.value = value); const setIsFullScreen = (value: boolean) => (isFullScreen.value = value);
const setIsRowDrag = (value: boolean) => (isRowDrag.value = value); const setIsRowDrag = (value: boolean) => (isRowDrag.value = value);
const setHighlightCurrentRow = (value: boolean) => (highlightCurrentRow.value = value);
return { return {
tableSize, tableSize,
@@ -45,6 +48,8 @@ export const useTableStore = defineStore(
setIsFullScreen, setIsFullScreen,
isRowDrag, isRowDrag,
setIsRowDrag, setIsRowDrag,
highlightCurrentRow,
setHighlightCurrentRow,
}; };
}, },
{ {
+3 -6
View File
@@ -161,17 +161,14 @@
// 元素全屏 // 元素全屏
.el-full-screen { .el-full-screen {
position: fixed; position: fixed;
top: 0; inset: 0;
right: 0;
left: 0;
z-index: 2300; z-index: 2300;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100vw !important; width: 100vw;
height: 100% !important;
padding: 15px; padding: 15px;
margin-top: 0; margin: 0;
background-color: var(--default-box-color); background-color: var(--default-box-color);
} }
+2
View File
@@ -114,6 +114,8 @@ export interface FastEnterBaseItem {
routeQuery?: Record<string, string>; routeQuery?: Record<string, string>;
/** 外部链接 */ /** 外部链接 */
link?: string; link?: string;
/** 点击弹出介绍弹窗(代替路由跳转) */
isDialog?: boolean;
} }
// 快速入口应用项 // 快速入口应用项
@@ -3,10 +3,9 @@
export const WEB_LINKS = { export const WEB_LINKS = {
GITHUB_HOME: "https://github.com/fastapiadmin", GITHUB_HOME: "https://github.com/fastapiadmin",
GITHUB: "https://github.com/fastapiadmin/FastapiAdmin", GITHUB: "https://github.com/fastapiadmin/FastapiAdmin",
/** 备用镜像 / 国内仓库(与旧版顶栏一致时可指向业务 Gitee) */
GITEE: "https://gitee.com/fastapiadmin/FastapiAdmin", GITEE: "https://gitee.com/fastapiadmin/FastapiAdmin",
BLOG: "https://blog.csdn.net/weixin_46768253?type=blog", BLOG: "https://blog.csdn.net/weixin_46768253?type=blog",
DOCS: "https://service.fastapiadmin.com/overview/", DOCS: "https://service.fastapiadmin.com/guide/overview.html",
LiteVersion: "https://gitee.com/fastapiadmin/FastCloud", LiteVersion: "https://gitee.com/fastapiadmin/FastCloud",
OldVersion: "https://github.com/fastapiadmin/FastapiAdmin/tree/v2.0.0", OldVersion: "https://github.com/fastapiadmin/FastapiAdmin/tree/v2.0.0",
COMMUNITY: "https://service.fastapiadmin.com", COMMUNITY: "https://service.fastapiadmin.com",
@@ -14,6 +14,7 @@
:stripe="false" :stripe="false"
:header-cell-style="{ background: 'transparent' }" :header-cell-style="{ background: 'transparent' }"
> >
<ElTableColumn type="index" label="序号" width="60" />
<ElTableColumn prop="name" label="产品名称" width="200" /> <ElTableColumn prop="name" label="产品名称" width="200" />
<ElTableColumn prop="popularity" label="销量"> <ElTableColumn prop="popularity" label="销量">
<template #default="scope"> <template #default="scope">
@@ -1082,6 +1082,11 @@ const currentUser = {
.dashboard-container { .dashboard-container {
position: relative; position: relative;
:deep(.el-card) {
--el-card-border-radius: calc(var(--custom-radius) + 2px);
border: 1px solid var(--fa-card-border);
}
.github-corner { .github-corner {
position: absolute; position: absolute;
top: 0; top: 0;
@@ -1,7 +1,7 @@
<template> <template>
<FaBasicBanner <FaBasicBanner
class="justify-center !h-53 mb-5 max-sm:!pt-8 max-sm:!h-48 max-sm:mb-4" class="justify-center !h-53 mb-5 max-sm:!pt-8 max-sm:!h-48 max-sm:mb-4"
:title="`欢迎回来 ${userInfo?.name}`" :title="`欢迎回来 ${userInfo?.username}`"
boxStyle="!bg-theme/10" boxStyle="!bg-theme/10"
titleColor="var(--fa-gray-900)" titleColor="var(--fa-gray-900)"
:decoration="false" :decoration="false"
@@ -54,7 +54,7 @@ const userStore = useUserStore();
/** /**
* 获取当前用户信息 * 获取当前用户信息
*/ */
const userInfo = computed(() => userStore.getUserInfo); const userInfo = computed(() => userStore.basicInfo);
/** /**
* 处理横幅点击事件 * 处理横幅点击事件
@@ -16,6 +16,7 @@
:header-cell-style="{ background: 'transparent' }" :header-cell-style="{ background: 'transparent' }"
> >
<template #default> <template #default>
<ElTableColumn type="index" label="序号" width="60" />
<ElTableColumn label="产品" prop="product" width="220px"> <ElTableColumn label="产品" prop="product" width="220px">
<template #default="scope"> <template #default="scope">
<div class="flex-c"> <div class="flex-c">
@@ -24,6 +24,7 @@
:header-cell-style="{ background: 'transparent' }" :header-cell-style="{ background: 'transparent' }"
> >
<template #default> <template #default>
<ElTableColumn type="index" label="序号" width="60" />
<ElTableColumn label="头像" prop="avatar" width="150px"> <ElTableColumn label="头像" prop="avatar" width="150px">
<template #default="scope"> <template #default="scope">
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
@@ -125,7 +125,7 @@
</div> </div>
<!-- 聊天输入区域 --> <!-- 聊天输入区域 -->
<div class="p-4"> <div class="p-4 flex-shrink-0">
<ElInput <ElInput
v-model="messageText" v-model="messageText"
type="textarea" type="textarea"
@@ -171,7 +171,7 @@ import avatar9 from "@imgs/avatar/avatar9.webp";
import avatar10 from "@imgs/avatar/avatar10.webp"; import avatar10 from "@imgs/avatar/avatar10.webp";
import { useAutoLayoutHeight } from "@/hooks/core/useLayoutHeight"; import { useAutoLayoutHeight } from "@/hooks/core/useLayoutHeight";
defineOptions({ name: "TemplateChat" }); defineOptions({ name: "FastlinkFachat" });
const { containerMinHeight } = useAutoLayoutHeight(); const { containerMinHeight } = useAutoLayoutHeight();
@@ -381,6 +381,7 @@ const {
page_size: 10, page_size: 10,
}, },
columnsFactory: (): ColumnOption<DemoTable>[] => [ columnsFactory: (): ColumnOption<DemoTable>[] => [
{ type: "globalIndex", width: 56, label: "序号" },
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ prop: "id", label: "ID", width: 72 }, { prop: "id", label: "ID", width: 72 },
{ prop: "name", label: "名称", minWidth: 120, showOverflowTooltip: true }, { prop: "name", label: "名称", minWidth: 120, showOverflowTooltip: true },
@@ -277,6 +277,7 @@ const {
page_size: 10, page_size: 10,
}, },
columnsFactory: (): ColumnOption<Demo01Table>[] => [ columnsFactory: (): ColumnOption<Demo01Table>[] => [
{ type: "globalIndex", width: 56, label: "序号" },
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ prop: "name", label: "名称", minWidth: 120, showOverflowTooltip: true }, { prop: "name", label: "名称", minWidth: 120, showOverflowTooltip: true },
{ prop: "uuid", label: "UUID", minWidth: 168, showOverflowTooltip: true }, { prop: "uuid", label: "UUID", minWidth: 168, showOverflowTooltip: true },
+69 -8
View File
@@ -145,7 +145,7 @@
</div> </div>
</template> </template>
<div class="cache-table-wrap"> <div class="cache-table-wrap">
<ElTable :loading="loading" :data="cacheNames" row-key="cache_name" height="100%"> <ElTable :loading="loading" :data="cacheNames" row-key="cache_name">
<template #empty> <template #empty>
<ElEmpty :image-size="80" description="暂无数据" /> <ElEmpty :image-size="80" description="暂无数据" />
</template> </template>
@@ -219,7 +219,6 @@
:loading="subLoading" :loading="subLoading"
:data="cacheKeys.map((key) => ({ cacheKey: key }))" :data="cacheKeys.map((key) => ({ cacheKey: key }))"
row-key="cacheKey" row-key="cacheKey"
height="100%"
> >
<template #empty> <template #empty>
<ElEmpty :image-size="80" description="暂无数据" /> <ElEmpty :image-size="80" description="暂无数据" />
@@ -549,8 +548,32 @@ onUnmounted(() => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// 让 el-tabs 填满父容器高度
.fa-full-height {
:deep(.el-tabs) {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
> .el-tabs__content {
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
> .el-tab-pane {
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
overflow: hidden;
}
}
}
}
.monitor-tab { .monitor-tab {
box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;
@@ -560,7 +583,6 @@ onUnmounted(() => {
.monitor-charts-row { .monitor-charts-row {
flex: 1; flex: 1;
align-items: stretch;
min-height: 0; min-height: 0;
} }
@@ -586,12 +608,11 @@ onUnmounted(() => {
.chart-container { .chart-container {
flex: 1; flex: 1;
height: 100%;
min-height: 200px; min-height: 200px;
} }
// === 缓存管理三栏 ===
.cache-mgmt-tab { .cache-mgmt-tab {
box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
@@ -600,7 +621,6 @@ onUnmounted(() => {
.cache-mgmt-row { .cache-mgmt-row {
flex: 1; flex: 1;
align-items: stretch;
min-height: 0; min-height: 0;
} }
@@ -631,13 +651,34 @@ onUnmounted(() => {
} }
.cache-table-wrap { .cache-table-wrap {
position: relative;
overflow: hidden; overflow: hidden;
:deep(.el-table) {
position: absolute;
inset: 0;
height: auto;
.el-table__inner-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
.el-table__header-wrapper {
flex-shrink: 0;
}
.el-table__body-wrapper {
flex: 1;
overflow-y: auto;
}
}
} }
.cache-form-wrap { .cache-form-wrap {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
.el-form { .el-form {
display: flex; display: flex;
@@ -646,4 +687,24 @@ onUnmounted(() => {
min-height: 0; min-height: 0;
} }
} }
// 缓存内容 textarea 填满剩余空间,但不超过 60%
.cache-value-item {
flex: 1;
min-height: 0;
max-height: 60%;
:deep(.el-form-item__content) {
height: 100%;
.el-textarea {
height: 100%;
textarea {
height: 100% !important;
resize: none;
}
}
}
}
</style> </style>
@@ -161,6 +161,7 @@ const {
page_size: 10, page_size: 10,
}, },
columnsFactory: (): ColumnOption<OnlineUserTable>[] => [ columnsFactory: (): ColumnOption<OnlineUserTable>[] => [
{ type: "globalIndex", width: 56, label: "序号" },
{ {
prop: "session_id", prop: "session_id",
label: "会话编号", label: "会话编号",
@@ -1,46 +0,0 @@
<!-- 更新日志(平台右上角头像下拉入口) -->
<template>
<div class="mx-auto pt-5 mb-5">
<h3 class="text-2xl font-medium text-g-900 mb-8">更新日志</h3>
<div class="space-y-5">
<div
v-for="item in upgradeLogList"
:key="item.version"
class="fa-card-sm rounded-lg p-6 transition-shadow max-md:p-4"
>
<div class="flex-cb gap-3 mb-4 flex-wrap">
<span class="px-3 py-1 bg-theme/10 text-theme text-sm font-medium rounded-full">
{{ item.version }}
</span>
<span class="text-sm text-g-500">{{ item.date }}</span>
</div>
<h4 class="text-lg font-medium text-g-900 mb-3">{{ item.title }}</h4>
<ul v-if="item.detail?.length" class="space-y-2 mb-4">
<li
v-for="(detail, index) in item.detail"
:key="index"
class="flex-c gap-2 text-sm text-g-600"
>
<span class="mt-0.5">•</span>
<span class="text-g-700">{{ detail }}</span>
</li>
</ul>
<div v-if="item.remark" class="text-sm text-g-800 bg-g-300/60 rounded p-3 mb-3">
{{ item.remark }}
</div>
<ElTag v-if="item.requireReLogin" type="warning" size="small">需要重新登录</ElTag>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { upgradeLogList } from "@/mock/upgrade/changeLog";
defineOptions({ name: "SystemChangeLog" });
</script>
@@ -336,6 +336,7 @@ const opCtx = {
const { columnChecks, columns } = useTableColumns<DeptTable>(() => [ const { columnChecks, columns } = useTableColumns<DeptTable>(() => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ prop: "name", label: "部门名称", minWidth: 120, showOverflowTooltip: true }, { prop: "name", label: "部门名称", minWidth: 120, showOverflowTooltip: true },
{ prop: "code", label: "部门编码", minWidth: 120, showOverflowTooltip: true }, { prop: "code", label: "部门编码", minWidth: 120, showOverflowTooltip: true },
{ {
@@ -446,6 +446,7 @@ const {
}, },
columnsFactory: (): ColumnOption<DictDataTable>[] => [ columnsFactory: (): ColumnOption<DictDataTable>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ prop: "dict_label", label: "标签", minWidth: 150, showOverflowTooltip: true }, { prop: "dict_label", label: "标签", minWidth: 150, showOverflowTooltip: true },
{ {
prop: "status", prop: "status",
@@ -283,6 +283,7 @@ const {
}, },
columnsFactory: (): ColumnOption<DictTable>[] => [ columnsFactory: (): ColumnOption<DictTable>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ prop: "dict_name", label: "字典名称", minWidth: 140, showOverflowTooltip: true }, { prop: "dict_name", label: "字典名称", minWidth: 140, showOverflowTooltip: true },
{ {
prop: "dict_type", prop: "dict_type",
@@ -289,6 +289,7 @@ const {
}, },
columnsFactory: (): ColumnOption<LogTable>[] => [ columnsFactory: (): ColumnOption<LogTable>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ {
prop: "type", prop: "type",
label: "日志类型", label: "日志类型",
@@ -345,6 +345,7 @@ const {
}, },
columnsFactory: (): ColumnOption<NoticeTable>[] => [ columnsFactory: (): ColumnOption<NoticeTable>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ prop: "notice_title", label: "通知标题", minWidth: 140, showOverflowTooltip: true }, { prop: "notice_title", label: "通知标题", minWidth: 140, showOverflowTooltip: true },
{ {
prop: "status", prop: "status",
@@ -294,6 +294,7 @@ const {
}, },
columnsFactory: (): ColumnOption<ConfigTable>[] => [ columnsFactory: (): ColumnOption<ConfigTable>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ prop: "config_name", label: "配置名称", minWidth: 120, showOverflowTooltip: true }, { prop: "config_name", label: "配置名称", minWidth: 120, showOverflowTooltip: true },
{ prop: "config_key", label: "配置键", minWidth: 200, showOverflowTooltip: true }, { prop: "config_key", label: "配置键", minWidth: 200, showOverflowTooltip: true },
{ prop: "config_value", label: "配置值", minWidth: 200, showOverflowTooltip: true }, { prop: "config_value", label: "配置值", minWidth: 200, showOverflowTooltip: true },
@@ -452,6 +452,7 @@ const {
}, },
columnsFactory: (): ColumnOption<PositionTable>[] => [ columnsFactory: (): ColumnOption<PositionTable>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ prop: "name", label: "岗位名称", minWidth: 100, showOverflowTooltip: true }, { prop: "name", label: "岗位名称", minWidth: 100, showOverflowTooltip: true },
{ {
prop: "status", prop: "status",
@@ -459,6 +459,7 @@ const {
}, },
columnsFactory: (): ColumnOption<RoleTable>[] => [ columnsFactory: (): ColumnOption<RoleTable>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ prop: "name", label: "角色名称", minWidth: 100, showOverflowTooltip: true }, { prop: "name", label: "角色名称", minWidth: 100, showOverflowTooltip: true },
{ prop: "code", label: "角色编码", minWidth: 100, showOverflowTooltip: true }, { prop: "code", label: "角色编码", minWidth: 100, showOverflowTooltip: true },
{ {
@@ -411,6 +411,7 @@ const {
}, },
columnsFactory: (): ColumnOption<TenantTable>[] => [ columnsFactory: (): ColumnOption<TenantTable>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ prop: "name", label: "租户名称", minWidth: 140, showOverflowTooltip: true }, { prop: "name", label: "租户名称", minWidth: 140, showOverflowTooltip: true },
{ prop: "code", label: "租户编码", minWidth: 120, showOverflowTooltip: true }, { prop: "code", label: "租户编码", minWidth: 120, showOverflowTooltip: true },
{ {
@@ -616,6 +616,7 @@ const {
}, },
columnsFactory: (): import("@/types/component").ColumnOption<UserInfo>[] => [ columnsFactory: (): import("@/types/component").ColumnOption<UserInfo>[] => [
{ type: "selection", width: 48, fixed: "left" }, { type: "selection", width: 48, fixed: "left" },
{ type: "globalIndex", width: 56, label: "序号" },
{ {
prop: "avatar", prop: "avatar",
label: "头像", label: "头像",