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') // 注释掉这行
// }