企业管理系统如何实现自定义首页与千人千面?RuoYi Office 给出了完整方案

企业管理系统如何实现自定义首页与千人千面?RuoYi Office 给出了完整方案

🌐 文档地址ruoyioffice.com | 📦 源码1gitee.com/yqzy1688/ru... |📦 源码2gitee.com/yqzy1688/ru... |📦 源码3github.com/yuqing2026/... | 💬 微信:17156169080(备注「RuoYi Office」)
当你打开钉钉、飞书或企业微信时,你看到的首页和同事看到的一样吗?------答案几乎都是「不一样」。这就是千人千面,也是现代企业管理系统的标配能力。RuoYi Office 用一套优雅的架构设计,让 OA、CRM、ERP 等系统的首页定制变得触手可及。

引言:为什么企业管理系统需要自定义首页?

还记得那个年代吗?所有员工登录 OA 系统后,看到的都是同一个首页------公司公告、考勤统计、待办列表......不管你是销售总监还是仓库管理员,首页都是一模一样的。

这种「一刀切」的首页设计带来了哪些问题?

  • 信息过载:销售不需要看仓库库存,行政不关心客户线索,但大家被迫面对一堆无关信息
  • 效率低下:找到自己真正需要的功能,往往需要点击 3-5 次菜单
  • 体验割裂:不同角色的工作场景截然不同,却被迫使用同一套界面
  • 用户流失:糟糕的首页体验直接导致系统使用率下降

据 Gartner 研究报告,个性化的工作界面能提升员工工作效率 20% 以上,同时将系统使用满意度提升 40%。

这就是为什么钉钉、飞书等头部办公软件都在大力推进千人千面的首页定制能力------不同角色看到不同内容,每个人都能打造属于自己的工作台。

但对于中小企业来说,如何在自己的管理系统中实现这一能力呢?RuoYi Office 给出了一套完整、可复用的开源方案。

什么是「千人千面」?

千人千面(Personalized Experience)是指系统根据用户的角色、权限、偏好等因素,为每个用户呈现不同的首页内容和布局。

在企业管理系统中,千人千面通常包含三个层次:

层次 说明 示例
角色级定制 不同角色看到不同的默认首页 管理层看数据看板,销售看客户动态,HR 看人事统计
用户级定制 每个用户可以自定义自己的首页布局 拖拽调整组件位置和大小,添加/移除组件
应用级定制 用户可以自定义常用应用和快捷入口 自主选择常用功能,个性化排序

RuoYi Office 在这三个层次上都做了完整实现,下面我们将从核心设计思想、数据结构设计、后端服务实现到前端交互,逐层剖析。

核心设计思想

在动手写代码之前,RuoYi Office 团队确立了几个核心设计原则:

1. 页面即模板,布局即配置

首页不是硬编码的页面,而是一个可配置的模板系统 。每个首页由一系列组件 按照特定的布局排列而成,布局信息以 JSON 形式存储在数据库中。

2. 组件化 + 注册制

所有首页上可展示的功能区块(如待办列表、通知公告、统计卡片等)都被封装为独立组件 ,通过统一的注册表进行管理。新增一个首页功能,只需要开发一个 Vue 组件并注册即可,无需修改任何框架代码。

3. 系统默认 + 用户自定义的双轨模式

系统管理员设定一个默认首页 供所有用户使用,同时每个用户可以选择或创建自己的个性化首页。如果用户没有自定义首页,则自动回退到系统默认首页。

4. RBAC 权限贯穿始终

首页上展示的应用入口、组件内容都会根据用户的角色和权限进行过滤。销售角色的用户即使添加了 ERP 组件,如果没有对应权限,也不会显示相关数据。

关键数据结构设计

好的数据结构设计是系统成功的基石。RuoYi Office 围绕首页自定义设计了 6 张核心数据表,形成了一个清晰的分层架构。

ER 关系总览

各表详解

1. system_home_page:首页配置表

首页的「身份证」,每一行代表一个首页模板。

sql 复制代码
CREATE TABLE `system_home_page` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '首页ID',
  `name` varchar(100) NOT NULL COMMENT '首页名称',
  `code` varchar(50) NOT NULL COMMENT '首页编码',
  `description` varchar(500) DEFAULT NULL COMMENT '首页描述',
  `preview_image` varchar(255) DEFAULT NULL COMMENT '预览图',
  `is_default` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否默认首页',
  `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态(0停用 1启用)',
  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `idx_code`(`code`, `deleted`)
);

设计要点

  • code 字段唯一标识首页模板,系统保留 default_workspace 作为默认首页
  • is_default 标记系统默认首页,用于用户未自定义首页时的回退
  • tenant_id 支持多租户隔离,不同租户可以有不同的默认首页
2. system_home_page_layout:首页布局配置表

记录每个首页上组件的位置和大小------这是实现「拖拽自定义」的核心数据。

sql 复制代码
CREATE TABLE `system_home_page_layout` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '布局ID',
  `page_id` bigint NOT NULL COMMENT '首页ID',
  `component_code` varchar(100) NOT NULL COMMENT '组件编码',
  `position_x` int NOT NULL DEFAULT 0 COMMENT 'X坐标',
  `position_y` int NOT NULL DEFAULT 0 COMMENT 'Y坐标',
  `width` int NOT NULL DEFAULT 6 COMMENT '宽度(栅格数)',
  `height` int NOT NULL DEFAULT 4 COMMENT '高度(栅格数)',
  `config` text DEFAULT NULL COMMENT '组件配置(JSON)',
  `sort` int NOT NULL DEFAULT 0 COMMENT '排序',
  PRIMARY KEY (`id`),
  INDEX `idx_page_id`(`page_id`, `deleted`)
);

设计要点

  • 使用 24 列栅格系统,与前端 Grid Layout 完美对齐
  • position_xposition_y 精确控制组件在网格上的位置
  • config 以 JSON 格式存储组件的个性化配置(如统计卡片显示哪些指标)
3. system_home_component:组件定义表

定义系统中所有可用的首页组件,相当于一个「组件市场」。

sql 复制代码
CREATE TABLE `system_home_component` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '组件ID',
  `category_id` bigint NOT NULL COMMENT '分类ID',
  `name` varchar(100) NOT NULL COMMENT '组件名称',
  `code` varchar(50) NOT NULL COMMENT '组件编码',
  `component_path` varchar(255) NOT NULL COMMENT '组件路径',
  `default_width` int NOT NULL DEFAULT 12 COMMENT '默认宽度(网格列数1-24)',
  `default_height` int NOT NULL DEFAULT 4 COMMENT '默认高度(网格行数)',
  `config_schema` text DEFAULT NULL COMMENT '配置Schema(JSON格式)',
  `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态(0停用 1启用)',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `idx_code`(`code`, `deleted`)
);

设计要点

  • config_schema 用 JSON Schema 描述组件支持哪些配置项,前端据此动态生成配置表单
  • default_width/default_height 提供默认尺寸,拖入画布时自动应用
  • 通过 category_id 关联分类表,方便组件面板分组展示
4. system_home_app_configsystem_home_app_user:双层应用配置

这是实现「千人千面」应用入口的关键设计------系统级配置 + 用户级配置的双层架构:

角色 说明
system_home_app_config 系统管理员 定义系统默认的常用应用列表
system_home_app_user 普通用户 每个用户自定义的应用列表

用户首次访问时,系统自动将系统级配置复制为用户的个人配置,之后用户可以自由增删改排序。这种**「写时复制」(Copy-on-Write)**的设计,既保证了初始体验的一致性,又给了用户充分的自定义空间。

后端服务实现:三个核心 Service

1. HomePageServiceImpl:首页管理服务

这是首页管理的核心服务,负责首页的增删改查和用户首页关联。

最关键的方法------获取用户首页

java 复制代码
@Override
public HomePageDO getUserHomePage(Long userId) {
    // 先查询用户是否配置了首页
    UserHomePageDO userHomePage = userHomePageMapper.selectByUserId(userId);
    if (userHomePage != null) {
        return homePageMapper.selectById(userHomePage.getPageId());
    }
    // 如果用户未配置首页,返回默认首页
    return getDefaultHomePage();
}

这段代码完美体现了「用户优先,默认兜底」的设计理念------先查用户自定义的首页,没有则回退到系统默认首页。

布局保存------从 JSON 到数据表

java 复制代码
@Override
@Transactional(rollbackFor = Exception.class)
public void saveHomePageLayout(HomePageLayoutSaveReqVO saveReqVO) {
    // 校验首页是否存在
    HomePageDO homePage = validateHomePageExists(saveReqVO.getPageId());
    
    // 校验权限:只有创建者或管理员可以保存
    if (!"default_workspace".equals(homePage.getCode())) {
        validateCreatorPermission(homePage);
    }

    // 先删除该首页的所有布局
    layoutMapper.deleteByPageId(saveReqVO.getPageId());

    // 解析 JSON 并逐项保存
    JSONObject layoutConfig = JSONUtil.parseObj(saveReqVO.getLayoutJson());
    JSONArray items = layoutConfig.getJSONArray("items");
    if (items != null && !items.isEmpty()) {
        for (int i = 0; i < items.size(); i++) {
            JSONObject item = items.getJSONObject(i);
            HomePageLayoutDO layout = new HomePageLayoutDO();
            layout.setPageId(saveReqVO.getPageId());
            layout.setComponentCode(item.getStr("componentCode"));
            layout.setPositionX(item.getInt("x", 0));
            layout.setPositionY(item.getInt("y", 0));
            layout.setWidth(item.getInt("w", 6));
            layout.setHeight(item.getInt("h", 4));
            layout.setConfig(item.getStr("config", "{}"));
            layout.setSort(i);
            layoutMapper.insert(layout);
        }
    }
}

采用**「先删后插」**的策略保存布局,简单可靠。前端设计器将整个布局序列化为 JSON,后端解析后逐项持久化到数据库。

2. HomeAppUserServiceImpl:用户应用服务

这是实现「千人千面」应用中心的核心------每个用户看到的常用应用都不一样。

核心流程------获取我的应用列表

java 复制代码
@Override
public List<HomeAppUserRespVO> getMyAppList() {
    Long userId = SecurityFrameworkUtils.getLoginUserId();

    // 1. 查询用户的应用配置
    List<HomeAppUserDO> appUserList = appUserMapper
        .selectListByUserIdAndStatus(userId, CommonStatusEnum.ENABLE.getStatus());

    // 2. 如果用户没有配置,自动从系统默认初始化
    if (appUserList.isEmpty()) {
        initUserApp();
        appUserList = appUserMapper
            .selectListByUserIdAndStatus(userId, CommonStatusEnum.ENABLE.getStatus());
    }

    // 3. 根据用户权限过滤应用(RBAC 控制)
    Set<Long> userMenuIds = getUserMenuIds(userId);
    appUserList = appUserList.stream()
            .filter(app -> userMenuIds.contains(app.getMenuId()))
            .collect(Collectors.toList());

    // 4. 转换为 VO 并补充菜单路径信息
    // ...
}

这段代码揭示了千人千面的三层过滤逻辑

  1. 首次访问自动初始化:从系统配置复制一份到用户名下
  2. 权限过滤:只展示用户有权限访问的应用
  3. 个性化排序:用户可以自由调整应用顺序

权限过滤的实现

java 复制代码
private Set<Long> getUserMenuIds(Long userId) {
    // 获取用户的角色列表
    Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(userId);
    // 获取角色拥有的菜单ID列表
    Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleIds);
    // 过滤出启用的菜单类型
    List<MenuDO> menuList = menuService.getMenuList();
    return menuList.stream()
            .filter(menu -> menuIds.contains(menu.getId()))
            .filter(menu -> menu.getType().equals(2)) // 只选择菜单类型
            .filter(menu -> menu.getStatus().equals(CommonStatusEnum.ENABLE.getStatus()))
            .map(MenuDO::getId)
            .collect(Collectors.toSet());
}

通过 用户 → 角色 → 菜单 的链路,精确控制每个用户能看到的应用入口。这就是为什么同一个系统中,总经理和普通员工的应用中心展示完全不同。

3. HomeComponentServiceImpl:组件管理服务

管理首页可用的组件库------相当于一个「组件市场」的后端。

java 复制代码
@Override
public Map<Long, List<HomeComponentDO>> getComponentsByCategory() {
    List<HomeComponentDO> components = componentMapper.selectList(
            HomeComponentDO::getStatus, CommonStatusEnum.ENABLE.getStatus());
    return components.stream()
            .collect(Collectors.groupingBy(HomeComponentDO::getCategoryId));
}

组件按分类分组返回给前端,设计器中的组件面板据此渲染分类列表。

前端实现:从组件注册到拖拽设计

组件注册表------前端的「组件市场」

RuoYi Office 采用了一个简洁优雅的组件注册机制:

typescript 复制代码
// registry.ts - 组件注册表
export interface ComponentRegistryItem {
  code: string;       // 组件编码(与后端 system_home_component.code 对应)
  component: Component; // Vue 组件
  name: string;       // 组件名称
  description?: string; // 组件描述
}

const componentRegistry = new Map<string, ComponentRegistryItem>();

export function registerComponent(item: ComponentRegistryItem) {
  componentRegistry.set(item.code, item);
}

export function getComponent(code: string): Component | undefined {
  return componentRegistry.get(code)?.component;
}

注册一个新组件只需一行代码:

typescript 复制代码
// 注册欢迎组件
registerComponent({
  code: 'workbench_welcome',
  component: WorkbenchWelcome,
  name: '欢迎组件',
  description: '展示欢迎信息、用户信息和天气',
});

// 注册通知公告组件
registerComponent({
  code: 'workbench_notice',
  component: WorkbenchNotice,
  name: '通知公告',
  description: '展示系统通知公告列表',
});

// 注册应用中心组件
registerComponent({
  code: 'workbench_app_center',
  component: WorkbenchAppCenter,
  name: '应用中心',
  description: '展示常用应用,支持拖拽排序',
});

// 更多组件...(任务列表、日程、统计卡片等)

RuoYi Office 已经内置了 11 个首页组件,涵盖了企业办公的常见场景:

组件 编码 功能
欢迎组件 workbench_welcome 展示用户信息、天气、问候语
应用中心 workbench_app_center 常用应用快捷入口,支持拖拽排序
任务列表 workbench_task_list 待办/已办/抄送任务
通知公告 workbench_notice 系统通知和公告
我的日程 workbench_schedule 日历视图的日程管理
快捷导航 workbench_quick_nav 常用功能快捷入口
访问统计 analytics_visits 数据统计卡片
数据详情 analytics_visits_data 访问数据折线图
来源分析 analytics_visits_source 流量来源饼图
项目列表 workbench_project 项目卡片展示
动态列表 workbench_trends 最新动态时间线

布局渲染器------将配置变为页面

LayoutRenderer 组件负责将后端存储的布局数据渲染为实际页面,基于 grid-layout-plus 实现。

vue 复制代码
<script lang="ts" setup>
import { GridItem, GridLayout } from 'grid-layout-plus';
import ComponentWrapper from '../components/wrapper/component-wrapper.vue';

// 加载布局
async function loadLayout() {
  const layoutItems = await getHomePageLayoutList(props.pageId);
  
  // 后端数据 → 前端栅格布局
  layout.value = layoutItems.map((item) => ({
    i: `item-${item.id}`,
    x: item.positionX,
    y: item.positionY,
    w: item.width,
    h: item.height,
    componentCode: item.componentCode,
    config: item.config ? JSON.parse(item.config) : {},
    static: true, // 渲染模式:不可拖拽
  }));
}
</script>

<template>
  <GridLayout v-model:layout="layout" :col-num="24" :row-height="60"
    :is-draggable="false" :is-resizable="false">
    <GridItem v-for="item in layout" :key="item.i"
      :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i">
      <ComponentWrapper :component-code="item.componentCode" :config="item.config" />
    </GridItem>
  </GridLayout>
</template>

渲染流程非常清晰:

  1. 通过 API 获取当前首页的布局列表
  2. 将数据库中的 position_x/ywidth/height 映射为栅格参数
  3. 通过 ComponentWrapper 根据 componentCode 动态加载对应的 Vue 组件
  4. 渲染模式下设置 static: true,禁止拖拽和缩放

首页入口------智能路由

用户访问首页时,系统会智能决定展示哪个首页:

vue 复制代码
<script lang="ts" setup>
const previewPageId = computed(() => {
  const pageId = route.query.preview;
  return pageId ? Number(pageId) : null;
});

const currentPageId = computed(() => {
  return previewPageId.value || homePageInfo.value?.id;
});

async function loadHomePage() {
  homePageInfo.value = await getMyHomePage(); // 调用后端 getUserHomePage
}
</script>

<template>
  <!-- 如果是预览模式,显示预览横幅 -->
  <Alert v-if="previewPageId" message="预览模式" type="info" />
  
  <!-- 有首页配置时渲染布局 -->
  <LayoutRenderer v-if="currentPageId" :page-id="currentPageId" />
  
  <!-- 没有首页配置时引导用户去配置 -->
  <Button v-else type="primary" @click="goToManage">去配置首页</Button>
</template>

首页设计器------可视化拖拽编排

首页设计器是整个功能最复杂也最有价值的部分,采用经典的三栏布局

▲ RuoYi Office 首页设计器:左侧选组件、中间拖布局、右侧调配置

设计器的三栏分工:

区域 组件 功能
左侧 ComponentPanel 展示可用组件,点击即可添加到画布
中间 DesignerCanvas 拖拽画布,支持组件的拖动和缩放
右侧 ConfigPanel 选中组件后,展示该组件的配置项

智能组件放置算法

当用户从组件面板添加一个组件时,系统会自动寻找一个不重叠的位置:

typescript 复制代码
function findAvailablePosition(width: number, height: number) {
  const colNum = 24;
  
  if (layout.value.length === 0) return { x: 0, y: 0 };
  
  const maxY = Math.max(...layout.value.map((item) => item.y + item.h), 0);
  
  // 从上到下、从左到右扫描可用位置
  for (let y = 0; y <= maxY + 1; y++) {
    for (let x = 0; x <= colNum - width; x++) {
      const isOverlap = layout.value.some((item) => {
        return !(
          x + width <= item.x ||   // 在左边
          x >= item.x + item.w ||  // 在右边
          y + height <= item.y ||  // 在上边
          y >= item.y + item.h     // 在下边
        );
      });
      if (!isOverlap) return { x, y };
    }
  }
  
  return { x: 0, y: maxY + 1 }; // 兜底:放在最底部
}

保存布局时,设计器会将画布上所有组件的位置、大小、配置序列化为 JSON,通过 API 发送到后端:

typescript 复制代码
async function handleSave() {
  const layoutConfig = {
    items: layout.value.map((item) => ({
      x: item.x, y: item.y, w: item.w, h: item.h,
      componentCode: item.componentCode,
      config: item.config,
    })),
    colNum: 24,
    rowHeight: 60,
    margin: [globalConfig.value.margin, globalConfig.value.margin],
    containerPadding: [globalConfig.value.containerPadding, globalConfig.value.containerPadding],
  };

  await saveHomePageLayout({
    pageId: pageId.value,
    layoutJson: JSON.stringify(layoutConfig),
  });
}

功能截图展示

首页管理

管理员可以在「首页管理」中查看、创建和管理所有首页模板,支持设置默认首页。

▲ 首页管理:查看所有首页模板,可以创建新首页、设置默认首页

首页组件管理

管理系统中可用的所有首页组件,包括组件编码、分类、默认尺寸等信息。RuoYi Office 目前已内置 11 个开箱即用的首页组件,覆盖了企业办公的核心场景:

分类 组件名称 组件编码 功能说明
🏠 工作台 欢迎组件 workbench_welcome 显示登录用户头像、姓名、问候语和实时天气信息,打造个性化的开屏体验
🚀 工作台 应用中心 workbench_app_center 根据用户权限展示常用应用快捷入口,支持拖拽排序和自定义增删,是千人千面的核心组件
工作台 任务列表 workbench_task_list 集成 BPM 流程引擎,一站式展示我的单据、待办任务、已办任务和抄送我的
📢 工作台 通知公告 workbench_notice 实时展示系统通知和公司公告,支持未读标记和详情预览
📅 工作台 我的日程 workbench_schedule 以日历视图展示个人日程安排,支持日/周/月切换和待办事项管理
🔗 导航 快捷导航 workbench_quick_nav 展示常用功能的图标入口,支持自定义配置导航项
📊 数据统计 访问统计 analytics_visits 以卡片形式展示关键业务指标(访问量、用户数、下载量等)
📈 数据统计 数据详情 analytics_visits_data 以折线图展示数据趋势变化,支持多维度数据对比
🍩 数据统计 来源分析 analytics_visits_source 以饼图分析流量来源分布,直观展示各渠道占比
📋 列表 项目列表 workbench_project 以卡片形式展示项目信息,包含项目名称、描述、日期等
📰 列表 动态列表 workbench_trends 以时间线形式展示团队最新动态和操作记录

💡 这些组件的设计充分考虑了企业办公的实际需求:工作台类组件 满足日常办公,数据统计类组件 满足管理层决策,列表类组件 满足信息浏览。开发者还可以根据自身业务需求,快速扩展更多自定义组件(后文会详细介绍扩展方法)。

▲ 组件管理:定义系统中可用的首页组件,支持分类、配置 Schema

可视化首页设计器

通过拖拽式的设计器自由编排首页布局,所见即所得。

▲ 首页设计器:左侧选组件、中间拖布局、右侧调配置,实时预览效果

用户工作台效果

最终用户看到的个性化工作台,包含欢迎信息、应用中心、待办任务、通知公告、日程等。

▲ 用户工作台:每个用户看到的都是根据自己角色和偏好定制的首页

扩展一个新组件有多简单?

得益于组件化 + 注册制的设计,在 RuoYi Office 中新增一个首页组件只需要 3 步

第 1 步:开发 Vue 组件

dashboard/home/components/ 目录下新建你的组件文件:

vue 复制代码
<!-- my-custom-widget.vue -->
<template>
  <div class="p-4">
    <h3>我的自定义组件</h3>
    <p>{{ config.title || '默认标题' }}</p>
    <!-- 你的业务逻辑 -->
  </div>
</template>

<script lang="ts" setup>
defineProps<{ config: Record<string, any> }>();
</script>

第 2 步:注册到组件注册表

registry.ts 中添加一行注册代码:

typescript 复制代码
import MyCustomWidget from './custom/my-custom-widget.vue';

registerComponent({
  code: 'my_custom_widget',
  component: MyCustomWidget,
  name: '我的自定义组件',
  description: '这是一个自定义的首页组件',
});

第 3 步:在后台管理中添加组件记录

在「首页组件管理」中添加一条记录,填写组件编码 my_custom_widget、默认宽高等信息。

完成!现在你就可以在首页设计器中拖拽添加你的自定义组件了。

技术架构总结

层次 技术选型 说明
前端框架 Vue 3.5 + TypeScript 响应式、类型安全
UI 组件库 Ant Design Vue + Vben Admin 企业级 UI 组件
拖拽引擎 grid-layout-plus Vue3 网格拖拽布局
后端框架 Spring Boot 3.5 + MyBatis Plus 企业级 Java 后端
权限控制 Spring Security + RBAC 角色级权限过滤
数据存储 MySQL + JSON 配置 结构化 + 灵活配置
多租户 租户隔离 不同企业独立首页配置

与同类方案的对比

特性 RuoYi Office 钉钉/飞书 传统 OA 系统
首页自定义 ✅ 完整支持
相关推荐
TT_Close2 小时前
“啪啪啪”三下键盘,极速拉起你的 uni-app 项目!
vue.js·uni-app·前端工程化
柯南95272 小时前
Electron 无边框窗口拖拽实现
vue.js·electron
Java水解4 小时前
你真的会打印日志吗?基于 Spring Boot 的全方位日志指南
spring boot·后端
顾青4 小时前
仅仅一行 CSS,竟让 2000 个节点的页面在弹框时卡成 PPT?
前端·vue.js·性能优化
用户4099322502124 小时前
如何在Vue3中优化生命周期钩子性能并规避常见陷阱?
前端·vue.js·trae
Java水解4 小时前
Spring Boot 实战:MyBatis 操作数据库(上)
spring boot·后端
Xin_z_4 小时前
解决 el-link 点击锚点导致 URL 变化的问题
vue.js
敲敲了个代码5 小时前
vue文件自动生成路由会成为主流
开发语言·前端·javascript·vue.js·前端框架
程序员林北北5 小时前
【前端进阶之旅】typescriot的数据类型讲解(二)
前端·javascript·vue.js·react.js·typescript