Vue3.5 企业级管理系统实战(十九):菜单管理

篇幅原因,本节先探讨菜单管理页面增删改查相关功能,角色菜单,菜单权限,动态菜单等内容放在后面。

1 菜单 api

在 src/api/menu.ts 中添加菜单 api,代码如下:

//src/api/menu.ts
import service from "./config/request";
import type { ApiResponse } from "./type";export interface MenuData {id: number;title: string;path: string;icon: string;name: string;sort_id: number;parent_id: number;
}//获取全部菜单
export const getAllMenus = (): Promise<ApiResponse<MenuData[]>> => {return service.get("/access/menu");
};//删除指定菜单
export const removeMenuById = (id: number
): Promise<ApiResponse<MenuData[]>> => {return service.delete("/access/menu/" + id);
};//新增菜单
export const addMenu = (data: MenuData): Promise<ApiResponse<MenuData>> => {return service.post("/access/menu", data);
};//更新指定菜单
export const updateMenuById = (id: number,data: Partial<MenuData>
): Promise<ApiResponse<MenuData[]>> => {return service.put("/access/menu/" + id, data);
};//批量更新菜单
export const updateBulkMenu = (data: Partial<MenuData>[]
): Promise<ApiResponse> => {return service.patch("/access/menu/update", { access: data });
};

2 菜单 Store

在 src/store/menu.ts 中添加菜单相关方法,代码如下:

//src/store/menu.ts
import {getAllMenus,type MenuData,addMenu,removeMenuById,updateMenuById,updateBulkMenu as updateBulkMenuApi
} from "@/api/menu";
import { getRoleAccessByRoles } from "@/api/roleAccess";
import { generateTree, ITreeItemDataWithMenuData } from "@/utils/generateTree";/*** 树形菜单项数据结构,继承自MenuData并扩展了子菜单属性*/
export interface ITreeItemData extends MenuData {children?: ITreeItemData[];
}/*** 菜单状态管理数据结构*/
export interface IMenuState {menuList: Array<MenuData>; // 原始菜单列表数据menuTreeData: ITreeItemData[]; // 树形菜单数据(管理界面使用)authMenuList: MenuData[]; // 权限过滤后的菜单列表(侧边栏使用)authMenuTreeData: ITreeItemDataWithMenuData[]; // 权限过滤后的树形菜单数据(侧边栏使用)
}/*** 菜单管理状态库* 使用Pinia实现的菜单状态管理,包含菜单的增删改查及权限控制*/
export const useMenuStore = defineStore("menu", () => {// 定义响应式状态const state = reactive<IMenuState>({menuList: [],menuTreeData: [],authMenuList: [], // 侧边菜单需要的authMenuTreeData: []});/*** 获取全部菜单列表并转换为树形结构* 用于管理界面展示完整菜单树*/const getAllMenuList = async () => {const res = await getAllMenus();if (res.code == 0) {const { data } = res;state.menuList = data; // 获取的原始菜单列表数据state.menuTreeData = generateTree(data); // 将原始数据转换为树形结构}};/*** 添加新菜单* @param data - 新菜单数据* @returns 添加成功返回true,失败返回false*/const appendMenu = async (data: ITreeItemData) => {const res = await addMenu(data);if (res.code == 0) {const node = { ...res.data };state.menuList.push(node); // 添加到原始列表state.menuTreeData = generateTree(state.menuList); // 重新生成树形结构return true;}};/*** 删除菜单* @param data - 要删除的菜单数据(需包含id)* @returns 删除成功返回true,失败返回false*/const removeMenu = async (data: ITreeItemData) => {const res = await removeMenuById(data.id);if (res.code == 0) {const idx = state.menuList.findIndex((menu) => menu.id === data.id);state.menuList.splice(idx, 1); // 从原始列表中删除state.menuTreeData = generateTree(state.menuList); // 重新生成树形结构return true;}};/*** 批量更新菜单(主要用于更新排序)* 1. 更新顶级菜单的sortId为其在数组中的索引位置* 2. 移除菜单对象中的children属性避免干扰后端数据* @returns 更新成功返回true,失败返回false*/const updateBulkMenu = async () => {// 1.更新sortIdstate.menuTreeData.forEach((menu, idx) => (menu.sort_id = idx));// 2.删除子节点(后端存储不需要children字段)const menus = state.menuTreeData.map((menu) => {const temp = { ...menu };delete temp.children;return temp;});// 批量更新const res = await updateBulkMenuApi(menus);if (res.code == 0) {return true;}};/*** 更新单个菜单* @param data - 要更新的菜单数据(需包含id)* @returns 更新成功返回true,失败返回false*/const updateMenu = async (data: Partial<MenuData>) => {const res = await updateMenuById(Number(data.id), data);if (res.code === 0) {await getAllMenuList(); // 更新成功后重新获取完整菜单列表return true;}};/*** 获取管理员权限的全部菜单* 用于管理员用户侧边栏显示*/const getAllMenuListByAdmin = async () => {const res = await getAllMenus();if (res.code == 0) {const { data } = res;state.authMenuList = data; // 侧边栏菜单列表state.authMenuTreeData = generateTree(data, true); // 生成带权限标识的树形菜单}};/*** 根据角色获取对应的菜单权限* @param roles - 角色ID数组*/const getMenuListByRoles = async (roles: number[]) => {const res = await getRoleAccessByRoles(roles);if (res.code == 0) {const { data } = res;const access = data.access;state.authMenuList = access; // 侧边栏菜单列表(权限过滤后)state.authMenuTreeData = generateTree(access, true); // 生成带权限标识的树形菜单}};// 导出状态和方法return {getAllMenuList,state,appendMenu,removeMenu,updateBulkMenu,updateMenu,getAllMenuListByAdmin,getMenuListByRoles};
});

3 封装生成 Tree 的方法

在 src/utils/generateTree.ts 中封装生成 Tree 的方法,代码如下:

//src/utils/generateTree.ts 
import type { MenuData } from "@/api/menu";
import type { ITreeItemData } from "@/stores/menu";/*** 扩展树形菜单项数据结构,添加可选的meta元数据* 用于侧边栏菜单的路由配置和权限控制*/
export type ITreeItemDataWithMenuData = ITreeItemData & {meta?: { icon: string; title: string; [key: string]: string };
};/*** 映射表类型定义,用于快速查找节点* 键为菜单ID,值为树形菜单项数据*/
export type IMap = Record<number, ITreeItemDataWithMenuData>;/*** 将扁平的菜单列表转换为树形结构* @param list - 扁平的菜单数据列表* @param withMeta - 是否添加meta元数据,默认为false* @returns 树形结构的菜单数据*/
export const generateTree = (list: MenuData[], withMeta: boolean = false) => {// 第一步:构建映射表,快速查找节点const map = list.reduce((memo, current) => {// 复制当前节点数据const temp = { ...current };// 如果需要元数据,添加meta字段if (withMeta) {(temp as ITreeItemDataWithMenuData).meta = {title: current.title, // 菜单标题icon: current.icon // 菜单图标};}// 将节点添加到映射表中memo[current.id] = temp;return memo;}, {} as IMap);// 第二步:构建树形结构const tree: ITreeItemDataWithMenuData[] = [];list.forEach((item) => {const pid = item.parent_id; // 当前节点的父IDconst cur = map[item.id]; // 从映射表中获取当前节点// 如果存在父节点,则将当前节点添加到父节点的children中if (pid !== 0 || pid != null) {const parent = map[pid];if (parent) {const children = parent?.children || [];children.push(cur);parent.children = children;return;}}// 如果没有父节点或父节点不存在,则作为根节点tree.push(cur);});return tree;
};

4 菜单管理界面

4.1 刷新页面 hook 函数

在 src/hooks/useReloadPage.ts 中,封装刷新页面的钩子函数,代码如下:

//src/hooks/useReloadPage.ts
export const useReloadPage = () => {const { proxy } = getCurrentInstance()!;const reloadPage = async ({title = "是否刷新",message = "你确定"} = {}) => {try {await proxy!.$confirm(title, message);window.location.reload();} catch {proxy?.$message.warning("已经取消了刷新");}};return {reloadPage};
};

4.2 菜单管理页面

在 src/views/system/menu/index.vue 中添加菜单管理页面,代码如下:

//src/views/system/menu/index.vue
<template><div class="menu-container"><!-- 菜单树展示区域 --><el-card><template #header><el-button @click="handleCreateRootMenu">新增顶级菜单</el-button></template><div class="menu-tree"><!-- 可拖拽的树形菜单组件 --><el-tree:data="menus":props="defaultProps"@node-click="handleNodeClick":expand-on-click-node="false"highlight-currentdraggable:allow-drop="allowDrop":allow-drag="allowDrag"@node-drop="handleNodeDrop"><!-- 自定义树节点内容,包含操作按钮 --><template #default="{ node, data }"><p class="custom-item"><span>{{ data.title }} </span><span><el-button link @click="handleCreateChildMenu(data)">添加</el-button><el-button link @click="handleRemoveMenu(data)">删除</el-button></span></p></template></el-tree></div></el-card><!-- 菜单编辑区域 --><el-card class="edit-card"><template #header> 编辑菜单 </template><!-- 菜单编辑组件,选中节点后显示 --><editor-menuv-show="editData && editData.id":data="editData!"@updateEdit="handleUpdateEdit"/><span v-if="editData == null">从菜单列表选择一项后,进行编辑</span></el-card><!-- 右侧添加菜单面板 --><right-panel v-model="panelVisible" :title="panelTitle" :size="330"><add-menu @submit="submitMenuForm"></add-menu></right-panel></div>
</template><script lang="ts" setup>
import type { MenuData } from "@/api/menu";
import { useReloadPage } from "@/hooks/useReloadPage";
import { type ITreeItemData, useMenuStore } from "@/stores/menu";
import type Node from "element-plus/es/components/tree/src/model/node";// 处理菜单更新事件
const handleUpdateEdit = async (data: Partial<MenuData>) => {const r = await store.updateMenu(data);if (r) {proxy?.$message.success("菜单编辑成功");reloadPage(); // 刷新页面数据}
};// 控制节点是否可拖拽
const allowDrag = (draggingNode: Node) => {// 根节点不可拖拽return (draggingNode.data.parent_id !== 0 || draggingNode.data.parent_id != null);
};type DropType = "prev" | "inner" | "next";// 控制节点拖拽放置规则
const allowDrop = (draggingNode: Node, dropNode: Node, type: DropType) => {// 根节点只能作为兄弟节点,不能作为子节点if (draggingNode.data.parent_id === 0 ||draggingNode.data.parent_id == null) {return type !== "inner";}
};// 处理节点拖拽完成事件
const handleNodeDrop = () => {store.updateBulkMenu(); // 更新菜单排序
};// 页面刷新工具
const { reloadPage } = useReloadPage();
// 获取菜单状态管理
const store = useMenuStore();
// 计算属性:获取树形菜单数据
const menus = computed(() => store.state.menuTreeData);
// 初始化加载菜单数据
store.getAllMenuList();// 树形组件配置
const defaultProps = {children: "children",label: "title"
};// 菜单类型:0-顶级菜单 1-子菜单
const menuType = ref(0);
// 右侧面板显示控制
const panelVisible = ref(false);// 计算属性:面板标题
const panelTitle = computed(() => {return menuType.value === 0 ? "添加顶级节点" : "添加子节点";
});// 创建顶级菜单
const handleCreateRootMenu = () => {menuType.value = 0;panelVisible.value = true;
};// 父菜单数据
const parentData = ref<ITreeItemData | null>();// 创建子菜单
const handleCreateChildMenu = (data: ITreeItemData) => {menuType.value = 1;panelVisible.value = true;parentData.value = data;
};// 重置状态
const resetStatus = () => {panelVisible.value = false;parentData.value = null;reloadPage();
};// 生成菜单排序ID
const genMenuSortId = (list: ITreeItemData[]) => {if (list && list.length) {return list[list.length - 1].sort_id + 1;}return 0;
};// 添加顶级菜单
const handleAddRootMenu = async (data: MenuData) => {// 设置父ID和排序IDdata.parent_id = 0;data.sort_id = genMenuSortId(menus.value);let res = await store.appendMenu(data);if (res) {proxy?.$message.success("根菜单添加成功了");}
};const { proxy } = getCurrentInstance()!;// 生成子菜单数据
const genChild = (data: MenuData) => {const parent = parentData.value!;if (!parent.children) {parent.children = [];}data.sort_id = genMenuSortId(parent.children);data.parent_id = parent.id;return data;
};// 添加子菜单
const handleChildRootMenu = async (data: MenuData) => {const child = genChild(data);let res = await store.appendMenu(child);if (res) {proxy?.$message.success("子菜单添加成功了");}
};// 提交菜单表单
const submitMenuForm = async (data: MenuData) => {if (menuType.value === 0) {handleAddRootMenu({ ...data });} else {handleChildRootMenu({ ...data });}resetStatus();
};// 删除菜单
const handleRemoveMenu = async (data: MenuData) => {try {await proxy?.$confirm("你确定删除" + data.title + "菜单吗?", {type: "warning"});await store.removeMenu({...data});proxy?.$message.success("菜单删除成功");reloadPage();} catch {proxy?.$message.info("取消菜单");}
};// 当前编辑的菜单数据
const editData = ref({} as MenuData);// 处理节点点击事件
const handleNodeClick = (data: MenuData) => {editData.value = { ...data };
};
</script><style scoped>
.menu-container {@apply flex p-20px; /* 容器布局和内边距 */
}
.menu-tree {@apply min-w-500px h-400px overflow-y-scroll; /* 菜单树区域固定高度,溢出滚动 */
}
.custom-item {@apply flex items-center justify-between flex-1; /* 自定义菜单项样式,两端对齐 */
}
.edit-card {@apply flex-1 ml-15px; /* 编辑区域自适应宽度 */
}
</style>

4.3 addMenu 组件

在 src/views/system/menu/components/addMenu.vue 中编写菜单添加组件,代码如下:

//src/views/system/menu/components/addMenu.vue
<template><div class="editor-container" p-20px><el-form ref="editFormRef" :model="editData" label-width="80px"><el-form-item label="菜单标题"><el-input v-model="editData.title" placeholder="请输入用户名" /></el-form-item><el-form-item label="路径"><el-input v-model="editData.path" placeholder="请输入邮箱" /></el-form-item><el-form-item label="图标"><el-input v-model="editData.icon" placeholder="请输入邮箱" /></el-form-item><el-form-item label="路由name"><el-input v-model="editData.name" placeholder="请输入邮箱" /></el-form-item><el-form-item><el-button type="primary" @click="submitMenuForm">提交</el-button></el-form-item></el-form></div>
</template><script lang="ts" setup>
import type { FormInstance } from "element-plus";const emit = defineEmits(["submit"]);const editFormRef = ref<FormInstance | null>(null);
const editData = ref({title: "",path: "",icon: "",name: ""
});// 提交编辑菜单
const submitMenuForm = () => {(editFormRef.value as FormInstance).validate((valid) => {if (valid) {emit("submit", editData.value);editData.value = {title: "",path: "",icon: "",name: ""};}});
};
</script>

4.4 editorMenu 组件

在 src/views/system/menu/components/editorMenu.vue 中编写菜单编辑组件,代码如下:

//src/views/system/menu/components/editorMenu.vue
<template><div class="editor-container"><el-formref="editFormRef":model="editData":rules="menuFormRules"label-width="100px"><el-form-item label="菜单名称" prop="title"><el-input v-model="editData.title" placeholder="请输入菜单名称" /></el-form-item><el-form-item label="路径" prop="path"><el-input v-model="editData.path" placeholder="请输入路由路径" /></el-form-item><el-form-item label="路由Name" prop="name"><el-input v-model="editData.name" placeholder="请输入路由名称" /></el-form-item><el-form-item label="图标" prop="icon"><el-input v-model="editData.icon" placeholder="请输入icon名称" /></el-form-item><el-form-item><el-button type="primary" @click="submitMenuForm">编辑菜单</el-button><el-button @click="submitReset">重置</el-button></el-form-item></el-form></div>
</template><script lang="ts" setup>
import type { MenuData } from "@/api/menu";
import type { FormInstance } from "element-plus";
// 编辑的数据
const editData = ref({id: -1,title: "",name: "",path: "",icon: ""
});
// 验证规则
const menuFormRules = {title: {required: true,message: "请输入菜单名称",trigger: "blur"},path: {required: true,message: "请输入路由路径",trigger: "blur"},name: {required: true,message: "请输入路由名称",trigger: "blur"}
};
const editFormRef = ref<FormInstance | null>(null);
const loading = ref(false);const resetFormData = (data: MenuData) => {editData.value = { ...editData.value, ...data };
};
const props = defineProps({data: {type: Object as PropType<MenuData>,required: true}
});
watch(() => props.data,(value) => {if (value) {resetFormData(value);}}
);
const submitReset = () => resetFormData(props.data);
const emit = defineEmits(["updateEdit"]);
const submitMenuForm = () => {(editFormRef.value as FormInstance).validate((valid) => {if (valid) {loading.value = true;emit("updateEdit", { ...editData.value });}});
};
</script>

以上,就是菜单管理的相关内容。

下一篇将继续探讨 角色菜单的实现,敬请期待~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83806.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【android bluetooth 协议分析 01】【HCI 层介绍 8】【ReadLocalVersionInformation命令介绍】

1. HCI_Read_Local_Version_Information 命令介绍 1. 功能&#xff08;Description&#xff09; HCI_Read_Local_Version_Information 命令用于读取本地 Bluetooth Controller 的版本信息&#xff0c;包括 HCI 和 LMP 层的版本&#xff0c;以及厂商 ID 和子版本号。 这类信息用…

React底层架构深度解析:从虚拟DOM到Fiber的演进之路

一、虚拟DOM&#xff1a;性能优化的基石 1.1 核心工作原理 React通过JSX语法将组件转换为轻量级JavaScript对象&#xff08;即虚拟DOM&#xff09;&#xff0c;而非直接操作真实DOM。这一过程由React.createElement()实现&#xff0c;其结构包含元素类型、属性和子节点等信息&a…

从AlphaGo到ChatGPT:AI技术如何一步步改变世界?

从AlphaGo到ChatGPT&#xff1a;AI技术如何一步步改变世界&#xff1f; 这里给大家分享一个人工智能学习网站。点击跳转到网站。 https://www.captainbed.cn/ccc 前言 在科技发展的历史长河中&#xff0c;人工智能&#xff08;AI&#xff09;技术无疑是最为璀璨的明珠之一。从…

关于在Unity项目中使用Post Processing插件打包到web端出现的问题

关于在Unity项目中使用Post Processing插件打包到web端出现的问题 解决方法&#xff1a;是不激活摄像机上的Post Processing有关组件&#xff0c;拉低场景中的Directional Light平行光的强度进行web端打包。 &#xff08;烘焙灯光时是可以激活。&#xff09; web端支持这个Pos…

MySQL - 如何突破单库性能瓶颈

数据库服务器硬件优化 我们来看看对数据库所在的服务器是如何进行优化的&#xff0c;服务器是数据库的宿主&#xff0c;其性能直接影响了数据库的性能&#xff0c;所以服务器的优化也是数据库优化的第一步。 数据库服务器通常是从 CPU、内存、磁盘三个角度进行硬件优化的&…

用 CodeBuddy 搭建「MiniGoal 小目标打卡器」:一次流畅的 UniApp 开发体验

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 在日常生活中&#xff0c;我们总是希望能够坚持一些小习惯&#xff0c;比如每天锻炼十分钟、读一页书、早睡十分…

OpenCV 环境搭建与概述

// //OpenCV-4.11.0 C VS2019 // 一、OpenCV学习路线 1、入门: OpenCV图像读写、视频读写、基本像素处理、基本卷积处理、基本C开发知识。 2、初级: OpenCV自定义卷积操作、图像梯度、边缘提取、二值分析、视频分析、形态学处理、几何变换与透视变换。 3、中级: 角点查找、BL…

如何快速更换电脑浏览器ip:教程与注意事项

无论是为了访问地域限制内容、保护隐私&#xff0c;还是解决网络问题&#xff0c;快速更换浏览器IP地址的需求日益增多。以下是快速更换电脑浏览器IP地址的几种常用方法及注意事项&#xff0c;结合了多种场景下的解决方案&#xff1a; 一、快速更换浏览器IP的方法 1. 代理服务…

【kafka】kafka概念,使用技巧go示例

1. Kafka基础概念 1.1 什么是Kafka&#xff1f; Kafka是一个分布式流处理平台&#xff0c;用于构建实时数据管道和流式应用。核心特点&#xff1a; 高吞吐量&#xff1a;每秒可处理百万级消息持久化存储&#xff1a;消息按Topic分区存储在磁盘分布式架构&#xff1a;支持水平…

掌握Git:版本控制与高效协作指南

一、初始Git 提出问题&#xff1a;无论是在工作还是学习&#xff0c;我们在编写各种文档的时候&#xff0c;更改失误&#xff0c;失误后恢复到原来版本&#xff0c;不得不复制出一个副本。 每个版本由各自的内容&#xff0c;但最终只有一个报告需要被我们使用。 但在此之前的…

【生活相关-日语-日本-东京-搬家后-引越(ひっこし)(3)-踩坑点:国民健康保险】

【生活相关-日语-日本-东京-搬家后-引越&#xff08;ひっこし&#xff09;&#xff08;3&#xff09;-注意点&#xff1a;国民健康保险】 1、前言2、情况说明&#xff08;1&#xff09;问题说明&#xff08;2&#xff09;情况说明&#xff08;1&#xff09;收到情况&#xff08…

linux——mysql故障排查与生产环境优化

目录 一&#xff0c;mysql数据库常见的故障 1&#xff0c;故障现象1 2&#xff0c;故障现象2 3&#xff0c;故障现象3 &#xff14;&#xff0c;故障现象&#xff14; &#xff15;&#xff0c;故障现象&#xff15; &#xff16;&#xff0c;故障现象&#xff16; 二&…

【C#】用 DevExpress 创建带“下拉子表”的参数表格视图

展示如何用 DevExpress 创建带“下拉子表”的参数表格视图。主表为 参数行 ParamRow&#xff0c;子表为 子项 ChildParam。 一、创建模型类 public class ParamRow {public string Pn { get; set; }public string DisplayName { get; set; }public string Value { get; set; }…

【JavaScript】用 Proxy 拦截对象属性

目录 一、Proxy 的基本结构&#xff08;打地基&#xff09; 二、最常用的两个拦截方法&#xff1a;get 和 set 1. get(target, key) 2. set(target, key, value) 三、说到这&#xff0c;那就可以回到题目来 四、什么是 Reflect&#xff1f; 总结不易&#xff0c;本章节对…

[IMX] 02.GPIO 寄存器

目录 手册对应章节 1.GPIO 复用&#xff08;引脚功能选择&#xff09;- IOMUXC_SW_MUX_CTL_PAD_xxx 2.GPIO 电气特性 - IOMUXC_SW_PAD_CTL_PAD_xxx 3.GPIO 数据与控制寄存器 3.1.数据 - DR 3.2.输入/输出选择 - GDIR 3.3.状态 - PSR 3.4.中断触发控制 - ICR 3.5.中断使…

Tomcat 配置 HTTPS 访问全攻略(CentOS 环境)

Tomcat 配置 HTTPS 访问全攻略&#xff08;CentOS 环境&#xff09; 一、环境说明 操作系统&#xff1a;CentOS Tomcat 版本&#xff1a;Apache Tomcat/9.0.105 服务器 IP&#xff1a;192.168.1.35 目标&#xff1a;将 Tomcat 默认的 HTTP 访问升级为 HTTPS&#xff0c;提…

Flink 运维监控与指标采集实战(Prometheus + Grafana 全流程)

一、引言:为什么 Flink 运维监控如此重要? 在实时计算场景中,Flink 作业 724 小时运行,对性能、资源、故障感知、状态变化的实时监控非常关键。没有有效的运维可观测体系: 不知道任务是否在稳定运行 发生问题难以快速定位 无法感知背压、延迟、反压等状态 因此,构建完善…

【prometheus+Grafana篇】基于Prometheus+Grafana实现Oracle数据库的监控与可视化

&#x1f4ab;《博主主页》&#xff1a; &#x1f50e; CSDN主页 &#x1f50e; IF Club社区主页 &#x1f525;《擅长领域》&#xff1a;擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对SQLserver、NoSQL(MongoDB)有了…

【数据仓库面试题合集③】实时数仓建模思路与实践详解

实时数据仓库已经成为各大企业构建核心指标监控与业务实时洞察的基础能力。面试中,关于实时建模的题目频繁出现,尤其聚焦于建模思路、宽表设计、状态管理、乱序处理等方面。本文整理典型题目及答题思路,帮助你应对相关考察。 一、建模原则与数仓分层认知 1. 实时数仓与离线…

鸿蒙PC操作系统:从Linux到自研微内核的蜕变

鸿蒙PC操作系统是否基于Linux内核,需要结合其技术架构、发展阶段和官方声明综合分析。以下从多个角度展开论述: 一、鸿蒙操作系统的多内核架构设计 多内核混合架构 根据资料,鸿蒙操作系统(HarmonyOS)采用分层多内核架构,内核层包含Linux内核、LiteOS-m内核、LiteOS-a内核…