Ruoyi 前端Vue3不使用网络本地数据示例

1. Ruoyi 前端Vue3不使用网络本地数据示例

1.1. 简介

  若依前后端分离版本的入口页面配置涉及环境准备、数据库配置、前后端启动三个核心环节。以下是详细配置步骤。

  若依前后端分离版本在不依赖网络和本地数据(纯模拟/离线场景)下,可通过前端Mock数据+后端模拟接口+本地文件存储实现完整流程。以下是具体配置与示例:

1.2. 核心思路

  若依 Vue3 项目结构,以下是若依前后端分离 Vue3 版本在不使用网络和数据库情况下的完整本地数据示例,采用 Mock 数据 + localStorage 本地存储 实现离线运行。

  通过Mock工具模拟前端数据请求、后端模拟接口返回固定数据、本地文件存储替代数据库,实现无需真实数据库和网络接口的完整系统运行。

方案 说明

前端 Mock 使用 Mock.js 拦截所有 API 请求,返回模拟数据
本地存储 使用 localStorage 持久化用户数据、配置等
无后端依赖 不启动 SpringBoot 后端,纯前端运行
无数据库 所有数据存储在浏览器本地

1.2.1. 下载若依 Vue3 前端项目

c 复制代码
# 方式一:Git 克隆(推荐)
git clone https://gitee.com/y_project/RuoYi-Vue3.git

# 方式二:下载 ZIP 包
# 访问若依官网 → 选择 Vue3 版本 → 下载 ZIP 解压

1.2.2. 安装依赖

c 复制代码
cd RuoYi-Vue3/ruoyi-ui
npm install --registry=https://registry.npmmirror.com

1.3. 前端 Mock 数据配置(Vue3 版本)

1.3.1. 安装 Mock 依赖

c 复制代码
npm install mockjs --save-dev

1.3.2. 创建 Mock 目录结构

1.3.3. Mock 主入口文件 src/mock/index.js

c 复制代码
import Mock from 'mockjs'
import { setupMock } from '@/utils/mock'
import * as user from './user'
import * as menu from './menu'
import * as dict from './dict'
import * as business from './business'

// 设置 Mock 全局配置
Mock.setup({
  timeout: '200-500' // 模拟网络延迟 200-500ms
})

// 注册所有 Mock 模块
setupMock({
  modules: [user, menu, dict, business]
})

console.log('✅ Mock 数据已加载,开发环境已启用模拟接口')

1.3.4. 用户相关接口 src/mock/user.js

c 复制代码
import { setupMock } from '@/utils/mock'
import Mock from 'mockjs'

// 从 localStorage 获取或生成用户数据
const getUserData = () => {
  const stored = localStorage.getItem('ruoyi_user_data')
  if (stored) {
    return JSON.parse(stored)
  }
  // 生成默认用户数据
  const userData = {
    admin: {
      userId: 1,
      loginName: 'admin',
      nickName: '管理员',
      email: 'admin@ruoyi.vip',
      phone: '13800138000',
      avatar: 'https://ruoyi.vip/assets/profile.jpg',
      roles: ['超级管理员'],
      permissions: [
        'system:user:add',
        'system:user:edit',
        'system:user:remove',
        'system:role:add',
        'system:role:edit',
        'system:menu:add',
        'system:menu:edit'
      ],
      status: 1
    },
    test: {
      userId: 2,
      loginName: 'test',
      nickName: '测试用户',
      email: 'test@ruoyi.vip',
      phone: '13900139000',
      avatar: 'https://ruoyi.vip/assets/profile.jpg',
      roles: ['普通用户'],
      permissions: ['system:user:view'],
      status: 1
    }
  }
  localStorage.setItem('ruoyi_user_data', JSON.stringify(userData))
  return userData
}

export default setupMock({
  mock: [
    // 模拟用户登录
    {
      url: '/prod-api/login',
      method: 'post',
      response: ({ body }) => {
        const { username, password } = body
        const users = getUserData()
        
        if (users[username] && password === (username === 'admin' ? 'admin123' : 'test123')) {
          const user = users[username]
          // 保存登录状态到 localStorage
          localStorage.setItem('ruoyi_token', Mock.Random.guid())
          localStorage.setItem('ruoyi_user_info', JSON.stringify(user))
          
          return {
            code: 200,
            msg: '登录成功',
            data: {
              token: localStorage.getItem('ruoyi_token'),
              userInfo: user
            }
          }
        } else {
          return {
            code: 500,
            msg: '用户名或密码错误',
            data: null
          }
        }
      }
    },

    // 获取用户信息(从 localStorage)
    {
      url: '/prod-api/system/user/getInfo',
      method: 'get',
      response: () => {
        const stored = localStorage.getItem('ruoyi_user_info')
        if (stored) {
          return {
            code: 200,
            data: JSON.parse(stored)
          }
        }
        return {
          code: 401,
          msg: '未登录',
          data: null
        }
      }
    },

    // 获取用户列表(从 localStorage 或生成)
    {
      url: '/prod-api/system/user/list',
      method: 'get',
      response: ({ query }) => {
        const { pageNum = 1, pageSize = 10, userName, phonenumber, status } = query
        
        // 检查 localStorage 是否有用户列表
        let userList = JSON.parse(localStorage.getItem('ruoyi_user_list') || 'null')
        
        if (!userList) {
          // 生成模拟用户数据
          userList = Mock.mock({
            'data|20': [
              {
                'userId|+1': 100,
                'loginName': '@cname',
                'nickName': '@cname',
                'email': '@email',
                'phonenumber': '13[89][0-9]{8}',
                'status': '@integer(0, 1)',
                'createTime': '@datetime'
              }
            ]
          }).data
          localStorage.setItem('ruoyi_user_list', JSON.stringify(userList))
        }
        
        // 简单过滤(实际可优化)
        const filtered = userList.filter(item => {
          if (userName && !item.loginName.includes(userName)) return false
          if (status !== undefined && item.status !== status) return false
          return true
        })
        
        // 分页处理
        const start = (pageNum - 1) * pageSize
        const end = start + pageSize
        const paginated = filtered.slice(start, end)
        
        return {
          code: 200,
          msg: 'success',
          data: {
            rows: paginated,
            total: filtered.length
          }
        }
      }
    },

    // 获取用户详情
    {
      url: '/prod-api/system/user/info/:userId',
      method: 'get',
      response: ({ params }) => {
        const users = getUserData()
        const user = Object.values(users).find(u => u.userId === parseInt(params.userId))
        
        if (user) {
          return {
            code: 200,
            data: user
          }
        }
        
        return {
          code: 404,
          msg: '用户不存在',
          data: null
        }
      }
    },

    // 添加用户(保存到 localStorage)
    {
      url: '/prod-api/system/user/add',
      method: 'post',
      response: ({ body }) => {
        let userList = JSON.parse(localStorage.getItem('ruoyi_user_list') || '[]')
        const newUser = {
          userId: userList.length > 0 ? Math.max(...userList.map(u => u.userId)) + 1 : 1,
          ...body,
          createTime: new Date().toLocaleString()
        }
        userList.push(newUser)
        localStorage.setItem('ruoyi_user_list', JSON.stringify(userList))
        
        return {
          code: 200,
          msg: '添加成功',
          data: newUser
        }
      }
    },

    // 修改用户(更新 localStorage)
    {
      url: '/prod-api/system/user/edit',
      method: 'put',
      response: ({ body }) => {
        let userList = JSON.parse(localStorage.getItem('ruoyi_user_list') || '[]')
        const index = userList.findIndex(u => u.userId === body.userId)
        
        if (index !== -1) {
          userList[index] = { ...userList[index], ...body }
          localStorage.setItem('ruoyi_user_list', JSON.stringify(userList))
          
          return {
            code: 200,
            msg: '修改成功',
            data: userList[index]
          }
        }
        
        return {
          code: 404,
          msg: '用户不存在',
          data: null
        }
      }
    },

    // 删除用户(从 localStorage 移除)
    {
      url: '/prod-api/system/user/delete/:userIds',
      method: 'delete',
      response: ({ params }) => {
        const userIds = params.userIds.split(',')
        let userList = JSON.parse(localStorage.getItem('ruoyi_user_list') || '[]')
        const filtered = userList.filter(u => !userIds.includes(String(u.userId)))
        
        localStorage.setItem('ruoyi_user_list', JSON.stringify(filtered))
        
        return {
          code: 200,
          msg: '删除成功',
          data: null
        }
      }
    }
  ]
})

1.3.5. 菜单相关接口 src/mock/menu.js

c 复制代码
import { setupMock } from '@/utils/mock'

// 从 localStorage 获取菜单(可持久化)
const getMenuData = () => {
  const stored = localStorage.getItem('ruoyi_menu_data')
  if (stored) {
    return JSON.parse(stored)
  }
  
  // 默认菜单数据
  const menuData = [
    {
      menuId: 1,
      menuName: '系统管理',
      path: '/system',
      component: 'layout/index',
      redirect: '/system/user',
      query: {},
      meta: {
        icon: 'setting',
        title: '系统管理',
        affix: false
      },
      children: [
        {
          menuId: 100,
          menuName: '用户管理',
          path: '/system/user',
          component: 'system/user/index',
          meta: {
            icon: 'peoples',
            title: '用户管理'
          }
        },
        {
          menuId: 101,
          menuName: '角色管理',
          path: '/system/role',
          component: 'system/role/index',
          meta: {
            icon: 'lock',
            title: '角色管理'
          }
        },
        {
          menuId: 102,
          menuName: '菜单管理',
          path: '/system/menu',
          component: 'system/menu/index',
          meta: {
            icon: 'tree-table',
            title: '菜单管理'
          }
        },
        {
          menuId: 103,
          menuName: '部门管理',
          path: '/system/dept',
          component: 'system/dept/index',
          meta: {
            icon: 'tree',
            title: '部门管理'
          }
        },
        {
          menuId: 104,
          menuName: '岗位管理',
          path: '/system/post',
          component: 'system/post/index',
          meta: {
            icon: 'post',
            title: '岗位管理'
          }
        }
      ]
    },
    {
      menuId: 2,
      menuName: '系统工具',
      path: '/tool',
      component: 'layout/index',
      redirect: '/tool/gen',
      query: {},
      meta: {
        icon: 'tool',
        title: '系统工具'
      },
      children: [
        {
          menuId: 200,
          menuName: '代码生成',
          path: '/tool/gen',
          component: 'tool/gen/index',
          meta: {
            icon: 'code',
            title: '代码生成'
          }
        },
        {
          menuId: 201,
          menuName: '系统监控',
          path: '/tool/druid',
          component: 'tool/druid/index',
          meta: {
            icon: 'chart',
            title: '系统监控'
          }
        }
      ]
    }
  ]
  
  localStorage.setItem('ruoyi_menu_data', JSON.stringify(menuData))
  return menuData
}

export default setupMock({
  mock: [
    // 获取菜单列表
    {
      url: '/prod-api/system/menu/list',
      method: 'get',
      response: () => {
        return {
          code: 200,
          data: getMenuData()
        }
      }
    },

    // 获取用户权限菜单
    {
      url: '/prod-api/system/menu/userPermissions',
      method: 'get',
      response: () => {
        const stored = localStorage.getItem('ruoyi_user_info')
        if (!stored) {
          return {
            code: 401,
            msg: '未登录',
            data: []
          }
        }
        
        const user = JSON.parse(stored)
        const allMenus = getMenuData()
        
        // 根据权限过滤菜单(简化版)
        if (user.roles.includes('超级管理员')) {
          return {
            code: 200,
            data: allMenus
          }
        }
        
        // 普通用户只显示部分菜单
        return {
          code: 200,
          data: [allMenus[0]]
        }
      }
    }
  ]
})

1.3.6. 字典数据接口 src/mock/dict.js

c 复制代码
import { setupMock } from '@/utils/mock'

// 从 localStorage 获取字典数据
const getDictData = () => {
  const stored = localStorage.getItem('ruoyi_dict_data')
  if (stored) {
    return JSON.parse(stored)
  }
  
  // 默认字典数据
  const dictData = [
    {
      dictId: 1,
      dictName: '用户性别',
      dictType: 'sys_user_sex',
      status: 0,
      remark: '用户性别列表',
      createTime: '2024-01-01 00:00:00',
      dictData: [
        {
          dictSort: 1,
          dictLabel: '男',
          dictValue: '0',
          dictType: 'sys_user_sex',
          cssClass: '',
          listClass: 'info',
          isDefault: 'N',
          status: 0
        },
        {
          dictSort: 2,
          dictLabel: '女',
          dictValue: '1',
          dictType: 'sys_user_sex',
          cssClass: '',
          listClass: 'primary',
          isDefault: 'N',
          status: 0
        },
        {
          dictSort: 3,
          dictLabel: '未知',
          dictValue: '2',
          dictType: 'sys_user_sex',
          cssClass: '',
          listClass: 'warning',
          isDefault: 'Y',
          status: 0
        }
      ]
    },
    {
      dictId: 2,
      dictName: '用户状态',
      dictType: 'sys_normal_disable',
      status: 0,
      remark: '系统开关状态',
      createTime: '2024-01-01 00:00:00',
      dictData: [
        {
          dictSort: 1,
          dictLabel: '正常',
          dictValue: '0',
          dictType: 'sys_normal_disable',
          cssClass: '',
          listClass: 'success',
          isDefault: 'Y',
          status: 0
        },
        {
          dictSort: 2,
          dictLabel: '停用',
          dictValue: '1',
          dictType: 'sys_normal_disable',
          cssClass: '',
          listClass: 'danger',
          isDefault: 'N',
          status: 0
        }
      ]
    },
    {
      dictId: 3,
      dictName: '系统开关',
      dictType: 'sys_normal_disable',
      status: 0,
      remark: '系统开关状态',
      createTime: '2024-01-01 00:00:00',
      dictData: [
        {
          dictSort: 1,
          dictLabel: '开启',
          dictValue: '0',
          dictType: 'sys_normal_disable',
          cssClass: '',
          listClass: 'success',
          isDefault: 'Y',
          status: 0
        },
        {
          dictSort: 2,
          dictLabel: '关闭',
          dictValue: '1',
          dictType: 'sys_normal_disable',
          cssClass: '',
          listClass: 'danger',
          isDefault: 'N',
          status: 0
        }
      ]
    }
  ]
  
  localStorage.setItem('ruoyi_dict_data', JSON.stringify(dictData))
  return dictData
}

export default setupMock({
  mock: [
    // 获取字典列表
    {
      url: '/prod-api/system/dict/type/list',
      method: 'get',
      response: () => {
        return {
          code: 200,
          data: getDictData()
        }
      }
    },

    // 根据字典类型获取字典数据
    {
      url: '/prod-api/system/dict/type/data/:dictType',
      method: 'get',
      response: ({ params }) => {
        const dicts = getDictData()
        const dict = dicts.find(d => d.dictType === params.dictType)
        
        if (dict) {
          return {
            code: 200,
            data: dict.dictData
          }
        }
        
        return {
          code: 404,
          msg: '字典类型不存在',
          data: []
        }
      }
    }
  ]
})

1.3.7. 业务数据接口 src/mock/business.js

c 复制代码
import { setupMock } from '@/utils/mock'
import Mock from 'mockjs'

export default setupMock({
  mock: [
    // 获取首页统计信息
    {
      url: '/prod-api/common/monitor/online',
      method: 'get',
      response: () => {
        return {
          code: 200,
          data: {
            total: 128,
            online: 86,
            offline: 42
          }
        }
      }
    },

    // 获取系统公告列表
    {
      url: '/prod-api/system/notice/list',
      method: 'get',
      response: () => {
        const stored = localStorage.getItem('ruoyi_notice_data')
        if (stored) {
          return {
            code: 200,
            data: JSON.parse(stored)
          }
        }
        
        const notices = {
          rows: [
            {
              noticeId: 1,
              noticeTitle: '系统维护通知',
              noticeContent: '系统将于今晚 22:00-24:00 进行维护升级,请提前保存数据。',
              noticeType: '1',
              status: 0,
              createBy: 'admin',
              createTime: '2024-01-15 10:00:00'
            },
            {
              noticeId: 2,
              noticeTitle: '新版本发布',
              noticeContent: '若依框架 v3.8.2 版本已发布,新增多项功能优化。',
              noticeType: '2',
              status: 0,
              createBy: 'admin',
              createTime: '2024-01-10 14:30:00'
            }
          ],
          total: 2
        }
        
        localStorage.setItem('ruoyi_notice_data', JSON.stringify(notices))
        return {
          code: 200,
          data: notices
        }
      }
    },

    // 获取操作日志列表
    {
      url: '/prod-api/system/log/logininfor/list',
      method: 'get',
      response: ({ query }) => {
        const { pageNum = 1, pageSize = 10 } = query
        
        const stored = localStorage.getItem('ruoyi_login_log_data')
        let logData = stored ? JSON.parse(stored) : null
        
        if (!logData) {
          logData = Mock.mock({
            'data|50': [
              {
                'infoId|+1': 1000,
                'userName': '@cname',
                ipaddr: '@ip',
                loginLocation: '@city',
                browser: '@browser',
                os: '@os',
                status: '@integer(0, 1)',
                msg: '@sentence',
                loginTime: '@datetime'
              }
            ]
          })
          localStorage.setItem('ruoyi_login_log_data', JSON.stringify(logData))
        }
        
        const start = (pageNum - 1) * pageSize
        const end = start + pageSize
        const paginated = logData.data.slice(start, end)
        
        return {
          code: 200,
          data: {
            rows: paginated,
            total: logData.data.length
          }
        }
      }
    },

    // 添加系统公告(保存到 localStorage)
    {
      url: '/prod-api/system/notice/add',
      method: 'post',
      response: ({ body }) => {
        let notices = JSON.parse(localStorage.getItem('ruoyi_notice_data') || '{"rows":[],"total":0}')
        const newNotice = {
          noticeId: notices.rows.length > 0 ? Math.max(...notices.rows.map(n => n.noticeId)) + 1 : 1,
          ...body,
          createBy: 'admin',
          createTime: new Date().toLocaleString()
        }
        notices.rows.push(newNotice)
        notices.total = notices.rows.length
        localStorage.setItem('ruoyi_notice_data', JSON.stringify(notices))
        
        return {
          code: 200,
          msg: '添加成功',
          data: newNotice
        }
      }
    }
  ]
})

1.3.8. Mock 工具函数 src/utils/mock.js

c 复制代码
import Mock from 'mockjs'

// 统一注册 Mock 模块
export function setupMock({ modules }) {
  modules.forEach(module => {
    if (module.mock) {
      module.mock.forEach(item => {
        const { url, method = 'get', response } = item
        Mock.mock(new RegExp(`^${url.replace(/:[a-zA-Z]+/g, '([^/]+)')}$`), method.toLowerCase(), (options) => {
          // 解析 URL 参数
          const params = {}
          const urlParams = url.match(/:[a-zA-Z]+/g) || []
          if (urlParams && options.url) {
            const pathParts = options.url.split('/')
            urlParams.forEach((param, index) => {
              const paramName = param.replace(':', '')
              params[paramName] = pathParts[index + 1]
            })
          }
          return response({ body: options.body, query: options.query, params })
        })
      })
    }
  })
}

export default setupMock

1.3.9. localStorage 封装工具 src/utils/storage.js

c 复制代码
// 通用存储工具类
class Storage {
  // 设置数据
  static set(key, value) {
    try {
      localStorage.setItem(key, JSON.stringify(value))
      return true
    } catch (e) {
      console.error('localStorage 存储失败:', e)
      return false
    }
  }

  // 获取数据
  static get(key, defaultValue = null) {
    try {
      const value = localStorage.getItem(key)
      return value ? JSON.parse(value) : defaultValue
    } catch (e) {
      console.error('localStorage 读取失败:', e)
      return defaultValue
    }
  }

  // 删除数据
  static remove(key) {
    try {
      localStorage.removeItem(key)
      return true
    } catch (e) {
      console.error('localStorage 删除失败:', e)
      return false
    }
  }

  // 清空所有数据
  static clear() {
    try {
      localStorage.clear()
      return true
    } catch (e) {
      console.error('localStorage 清空失败:', e)
      return false
    }
  }

  // 获取所有键
  static keys() {
    return Object.keys(localStorage)
  }
}

export default Storage

1.3.10. Axios 请求配置 src/utils/request.js

c 复制代码
import axios from 'axios'
import { ElMessage } from 'element-plus'
import { getToken } from '@/utils/auth'
import router from '@/router'

// 创建 axios 实例
const service = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API || '/prod-api',
  timeout: 15000,
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  }
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    const token = getToken()
    if (token) {
      config.headers['Authorization'] = 'Bearer ' + token
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code !== 200 && res.code !== 0) {
      ElMessage.error(res.msg || '请求失败')
      return Promise.reject(new Error(res.msg || 'Error'))
    }
    return res.data
  },
  error => {
    ElMessage.error(error.message || '请求失败')
    return Promise.reject(error)
  }
)

export default service

1.3.11. 认证工具 src/utils/auth.js

c 复制代码
import Storage from './storage'

const TokenKey = 'ruoyi_token'
const UserInfoKey = 'ruoyi_user_info'

export function getToken() {
  return Storage.get(TokenKey)
}

export function setToken(token) {
  return Storage.set(TokenKey, token)
}

export function removeToken() {
  return Storage.remove(TokenKey)
}

export function getUserInfo() {
  return Storage.get(UserInfoKey)
}

export function setUserInfo(info) {
  return Storage.set(UserInfoKey, info)
}

export function removeUserInfo() {
  return Storage.remove(UserInfoKey)
}

1.3.12. 主入口文件 src/main.js

c 复制代码
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
import router from './router'
import request from './utils/request'

// 开发环境加载 Mock
if (import.meta.env.DEV) {
  import('./mock')
}

const app = createApp(App)

// 注册 Element Plus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

app.use(ElementPlus)
app.use(router)
app.mount('#app')

console.log('✅ 若依 Vue3 项目已启动(Mock 模式)')

1.4. 完整运行流程

1.4.1. 启动前端项目

  打开浏览器访问 http://localhost:5173(或配置的其他端口)

1.4.2. 启动前端项目

c 复制代码
cd ruoyi-ui
npm install
npm run dev

1.4.3. 登录测试

账号 密码 说明
admin admin123 超级管理员
test test123 普通用户

1.4.4. 验证数据持久化

  (1)刷新页面后,登录状态、用户数据、菜单数据等仍保留在 localStorage 中。

  (2)清除浏览器缓存后,数据会重新生成。

1.5. 数据管理功能

1.5.1. 查看本地存储数据

  在浏览器控制台执行:

c 复制代码
// 查看所有存储的键
console.log(localStorage.keys())

// 查看用户数据
console.log(JSON.parse(localStorage.getItem('ruoyi_user_data')))

// 查看用户列表
console.log(JSON.parse(localStorage.getItem('ruoyi_user_list')))

1.5.2. 清除本地数据

c 复制代码
// 清除所有若依相关数据
localStorage.removeItem('ruoyi_user_data')
localStorage.removeItem('ruoyi_user_list')
localStorage.removeItem('ruoyi_menu_data')
localStorage.removeItem('ruoyi_dict_data')
localStorage.removeItem('ruoyi_notice_data')
localStorage.removeItem('ruoyi_login_log_data')
localStorage.removeItem('ruoyi_token')
localStorage.removeItem('ruoyi_user_info')

// 或清除所有
localStorage.clear()

1.6. 清除本地数据切换真实接口

  当后端接口完成后,只需注释掉 Mock 导入即可切换到真实接口:

c 复制代码
// src/main.js
// if (import.meta.env.DEV) {
//   import('./mock')  // 注释掉这行
// }
相关推荐
xrl20121 个月前
若依工作流主流方案对接
ruoyi·camunda·ruoyiflow
AI产品实战1 个月前
Ruoyi-Vue+WarmFlow实现三级加急审批
ruoyi
码银3 个月前
在若依框架中,使用easyExcel完成动态列导出
java·excel·ruoyi
AI产品实战3 个月前
95coder一句话生成MOM系统,AI用时6分50秒,Token只消耗25107
vue.js·spring boot·ai编程·ruoyi
gdhz-xwx3 个月前
RuoYi-Vue-Plus,前端VXE TABLE V4.13.7
ruoyi
RuoyiOffice3 个月前
2026 年开源 BPM/工作流引擎大盘点:Flowable vs Camunda vs Activiti vs Turbo——谁才是企业级首选?
java·spring boot·后端·开源·流程图·ruoyi·anti-design-vue
RuoyiOffice3 个月前
SpringBoot+Vue3 实现 OA 公文外来文与归档台账:外部收文、BPM办理、三类公文统一归档
spring boot·微服务·uni-app·vue·ruoyi·anti-design-vue·ruoyioffice
Nick_zcy3 个月前
小说在线阅读网站和小说管理系统 · 功能全解析
java·后端·python·springboot·ruoyi
天籁晴空3 个月前
Docker Compose 部署完整指南 -- RuoYi-Vue
docker·ruoyi