系统骨架和角色权限已经就绪,这一节直接把登录鉴权闭环打通。为了方便,直接启用云开发的手机号验证码登录——用户通过认证进入门户页后,我们再根据手机号拉取组织架构和权限信息,然后按角色控制不同工作台的入口。
[图片]
数据模型与变量
只列出本节直接相关的表,部门和岗位等其他表前面已经建好。
1. Users 表(用户信息表)
| 核心字段 | 标识 | 类型 | 说明 |
|---|---|---|---|
| 姓名 / 手机号 | name / phone | 单行文本 | 手机号与云开发认证绑定的唯一标识 |
| 部门 / 岗位 | dept_id / post_id | 关联关系 | 关联 Departments 和 Posts 表 |
| 最后登录 | last_login | 日期时间 | 门户初始化时自动更新 |
2. UserRoles 表(用户 - 角色关联表)
多对多映射:包含 user_id(关联 Users 表)和 role_id(关联 Roles 表)。
在首页中创建两个全局变量:currentUser(存储用户基础信息及部门、岗位)和 userRoles(存储角色编码集合),初始值分别是空对象和空数组。
[图片]
[图片]
门户初始化:构造 User 对象
云开发已经拦截了未登录用户,所以能进到门户页就说明手机号验证通过了。在全局方法 login 里完成剩下的工作。
[图片]
export default async function login({ event, data }) {
try {
$w.utils.showLoading({ title: '加载工作台中...' });
// 1. 从云开发原生 auth 对象获取当前手机号
const phone = $w.auth.currentUser?.phone || "15155667788";
if (!phone) {
return $w.utils.showToast({ title: '获取授权信息失败,请重新登录', icon: 'error' });
}
// 2. 根据手机号查用户信息(带关联部门、岗位)
const userRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_Users',
methodName: 'wedaGetRecordsV2',
params: {
filter: { where: { phone: { $eq: phone } } },
select: { $master: true, department_id: true, position_id: true }
}
});
if (!userRes.records.length) {
return $w.utils.showToast({ title: '该手机号未在系统中注册,请联系管理员', icon: 'error' });
}
const user = userRes.records[0];
console.log("user", user);
// 3. 异步更新最后登录时间,不阻塞后续流程
$w.cloud.callDataSource({
dataSourceName: 'MBA_Users',
methodName: 'wedaUpdateV2',
params: {
data: { last_login: Date.now() },
filter: { where: { _id: { $eq: user._id } } }
}
}).catch(e => console.error('更新登录时间失败', e));
// 4. 获取用户关联的角色
const roleRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_RoleUsers',
methodName: 'wedaGetRecordsV2',
params: {
filter: { where: { user_id: { $eq: user._id } } },
select: { $master: true, role_id: true }
}
});
console.log("roleRes", roleRes);
// 提取角色编码,如 ['ROLE_ADMIN', 'ROLE_TEACHER']
const roleCodes = roleRes.records.map(item => item.role_id?.code).filter(Boolean);
console.log("roleCodes", roleCodes);
// 5. 构造全局用户对象并写入页面状态
const userInfo = { ...user, deptInfo: user.department_id, postInfo: user.position_id };
$w.app.dataset.state.currentUser = userInfo;
$w.app.dataset.state.userRoles = roleCodes;
} catch (e) {
console.error('初始化门户失败', e);
$w.utils.showToast({ title: '系统加载失败,请刷新重试', icon: 'error' });
} finally {
$w.utils.hideLoading();
}
}
选中页面组件的 onShow 事件,调用这个 login 方法。
[图片]
路由控制:卡片跳转与拦截
门户页放置了不同工作台的入口卡片,点击时统一用下面的函数分发——根据传入的 data.target 检查 userRoles 里有没有对应角色,有就跳转,没有就提示。
export default function ({ event, data }) {
const userRoles = $w.app.dataset.state.userRoles || [];
const targetPortal = data.target;
const portalConfig = {
admin: { requiredRoles: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN'], pageId: 'u_bu_men_guan_li' },
employee: { requiredRoles: ['ROLE_USER', 'ROLE_DEPT_MANAGER'], pageId: 'employee-dashboard' },
teacher: { requiredRoles: ['ROLE_TEACHER'], pageId: 'teacher-dashboard' },
student: { requiredRoles: ['ROLE_STUDENT'], pageId: 'student-app' }
};
const config = portalConfig[targetPortal];
console.log("config", config);
if (!config) {
return $w.utils.showToast({ title: '无效的工作台入口', icon: 'error' });
}
const hasPermission = config.requiredRoles.some(role => userRoles.includes(role));
if (!hasPermission) {
return $w.utils.showToast({ title: '您没有该工作台的访问权限', icon: 'error' });
}
$w.utils.reLaunch({ pageId: config.pageId });
}
管理员入口的按钮绑定这个事件,传入 admin。
[图片]
认证部分完全由云开发接过去,业务层只做一次数据聚合拿到完整用户信息,再用角色编码做简单的包含判断,就实现了基于角色的入口控制。下一步进入具体工作台内部,处理业务数据流转。


