引言:校长的困惑——我的数据在哪里?
亲爱的校长们,在上一篇教程中,我们一起打好了教务管理系统的数据“地基”,并初步搭建了机构管理员的全局视角。现在,让我们把目光聚焦到每一所教培机构的“心脏”—— 校区 ,以及校区的核心管理者—— 校长 。
你可能面临这样的疑问: “我作为校长,只想管理我负责的那个校区的数据,低代码平台怎么知道哪个校区是我的?” “我登录系统后,是不是能看到所有校区的数据,那我怎么分辨哪些是我该管的?” “我的教务老师、招生老师,是不是也只能看到他们校区的数据?”
没错,这些是低代码平台在处理复杂权限和数据隔离时常遇到的“痛点”。由于低代码平台通常没有内置“当前用户所属校区”这种开箱即用的概念,我们需要通过一些巧妙的设计来实现数据的“专属感”。
本篇教程,我们将手把手带你解决这个核心问题,让校长登录后,看到的、管理的,都是他自己校区的专属数据!我们将围绕以下几个关键点展开:
登录后的“身份识别”:
全局变量的妙用:
数据过滤的魔法:
让我们开始,为校长打造一个真正属于自己的“专属地盘”!
第一部分:登录后的“身份识别与校区上下文加载”——让系统认识你!
低代码平台通常会提供一个内置的用户登录功能,并能在用户登录后获取到该用户的唯一标识(我们称之为 platform_user_id )。但这个ID,仅仅是平台账号,它并不知道你是哪个机构的哪个校区的校长。
为了解决这个问题,我们需要在校长登录后的第一时间,进行一次“身份认证”和“校区上下文加载”。
业务场景: 想象一下,校长登录系统后,系统首先会有一个欢迎页面,上面有一个大大的“进入系统”按钮。当校长点击这个按钮时,系统就开始加载他的专属数据。
1. 为什么需要引导页?
登录系统后,我们不能直接跳转到首页。因为首页上需要展示的数据(如学员列表、教师列表)是与校区关联的。如果不知道当前登录的是哪个校区的校长,就无法正确过滤数据。
这个引导页的作用,就是充当一个“中转站”,在进入业务页面前,完成以下核心任务:
获取平台账号信息:
查询校长身份:
根据平台账号,从 Principals 表中找到对应的校长记录。
关联校区信息:
根据校长记录,从 Campuses 表中找到他所负责的校区信息。
设置全局变量:
将这些关键的校区信息存储起来,供后续所有页面使用。
跳转到首页:
2. 引导页设计与核心逻辑
2.1 搭建页面布局
打开校长应用,点击创建页面的图标 输入页面的名称引导页 删除默认的网格布局,添加普通容器 切换到样式,点击CSS with ai,输入如下样式
:root { width: 100 % ; height: 100vh ; }
这样设置是为了让普通容器占满整个屏幕,然后设置普通容器的布局纵向排列,左对齐、垂直居中 普通容器里边添加文本组件和按钮组件,将文本内容修改为欢迎访问系统,按钮改为进入系统 设置文本组件的字体、行高和外边距 设置按钮的字号、内边距、圆角
2.2 创建全局变量
页面布局有了之后,我们需要创建一个全局变量来保存校长的身份信息,点击代码区的全局,创建变量 选择自定义变量 输入变量的名称,选择变量的类型为对象,输入初始的数据结构
{ "principal" : { "id" : "P001" , "platformUserId" : "user_abcxyz" , "name" : "张校长" } , "campus" : { "id" : "C001" , "name" : "市中心校区" } , "organization" : { "id" : "O001" , "name" : "优学教育集团" } }
2.3 创建API
全局变量的加载我们要通过定义一个API来获取数据,点击资源链接,点击+号创建API 选择自定义代码 输入API的名称 输入方法的名称和标识 添加入参 输入如下代码,点击方法测试
module.exports = async function ( params, context ) { const userId = params.userId ; // 获取入参中的 userId if ( ! userId ) { throw new Error ( 'User ID is required.' ) ; } let principalInfo = null ; let campusInfo = null ; let organizationInfo = null ; try { // 1 . 根据 userId 查询校长信息 ( 使用 wedaGetItemV2 获取单条 ) // 根据截图,校长表的数据模型标识为 'Principals' const principalResult = await context.callModel ( { dataSourceName: 'Principals' , // 数据模型标识为 Principals methodName: 'wedaGetItemV2' , // 获取单条数据 params: { select: { " $master " : true } , filter: { // 使用 filter 包裹 where where: { platform_user_id: { $eq : userId // 使用 $eq 比较运算符 } } } } , } ) ; console.log ( "principalResult" ,principalResult ) if ( principalResult && Object.keys ( principalResult ) .length > 0 ) { principalInfo = principalResult ; } else { // 如果没有找到对应的校长信息,则直接返回,表示该用户不是校长 return { principal: null, campus: null, organization: null, message: 'No principal found for the given user ID.' } ; } // 2 . 根据校长信息查询校区信息 ( 使用 wedaGetItemV2 获取单条 ) // 根据截图,校区表的数据模型标识为 'Campuses_jw' if ( principalInfo._id ) { // 确保获取到了 principal_id const campusResult = await context.callModel ( { dataSourceName: 'Campuses_jw' , // 数据模型标识为 Campuses_jw methodName: 'wedaGetItemV2' , // 因为一个校长只负责一个校区,所以用获取单条 params: { select: { " $master " : true } , filter: { // 使用 filter 包裹 where where: { principal_id: { $eq : principalInfo._id // 使用 $eq 比较运算符 } } } } , } ) ; console.log ( "campusResult" ,campusResult ) if ( campusResult && Object.keys ( campusResult ) .length > 0 ) { campusInfo = campusResult ; } } // 3 . 根据校区信息查询机构信息 ( 使用 wedaGetItemV2 获取单条 ) // 假设机构表的数据模型标识为 'Organizations' if ( campusInfo && campusInfo.org_id ) { // 确保获取到了校区信息和机构ID const organizationResult = await context.callModel ( { dataSourceName: 'Organizations' , // 假设机构表的数据模型标识 methodName: 'wedaGetItemV2' , // 获取单条数据 params: { select: { " $master " : true } , filter: { // 使用 filter 包裹 where where: { _id: { $eq : campusInfo.org_id // 使用 $eq 比较运算符 } } } } , } ) ; if ( organizationResult && Object.keys ( organizationResult ) .length > 0 ) { organizationInfo = organizationResult ; } } // 封装并返回最终结果 return { principal: { id: principalInfo._id, platformUserId: principalInfo.platform_user_id, name: principalInfo.full_name, // 假设校长表中的姓名是 full_name phone_number: principalInfo.phone_number, avatar_url: principalInfo.avatar_url, // 从校长表中获取头像URL email: principalInfo.email, // 从校长表中获取邮箱地址 // 根据实际需要添加更多校长属性 } , campus: campusInfo ? { id: campusInfo._id, name: campusInfo.campus_name, address: campusInfo.campus_address, contact_phone: campusInfo.contact_phone, campus_type: campusInfo.campus_type, // 从校区表中获取校区类型 campus_notice: campusInfo.campus_notice, // 从校区表中获取门店公告 campus_images_urls: campusInfo.campus_images_urls, // 从校区表中获取宣传图片URLs campus_video_url: campusInfo.campus_video_url, // 从校区表中获取校区视频URL detailed_intro: campusInfo.detailed_intro, // 从校区表中获取详细介绍 // 根据实际需要添加更多校区属性 } : null, organization: organizationInfo ? { id: organizationInfo._id, name: organizationInfo.org_name, org_logo_url: organizationInfo.org_logo_url, org_features: organizationInfo.org_features, // 根据实际需要添加更多机构属性 } : null } ; } catch ( error ) { console.error ( 'Error in getPrincipalContextByUserId API:' , error ) ; throw new Error ( ` Failed to get principal context: $ { error.message } ` ) ; } } ;
输入入参,执行成功后点击出参自动映射
2.4 创建自定义方法
API定义好之后,我们需要创建一个自定义方法来调用我们的API。在全局点击+,选择新建javascript方法 输入如下代码
export default async function getPrincipalInfo ( ) { try { $w .utils.showLoading ( { title: '加载中' , mask: false } ) ; const apiResult = await $w .cloud.callDataSource ( { dataSourceName: 'xzgl_tuk3kj4' , // API所属的数据源标识,用于后端API的查找和调用。 methodName: 'getPrincipalContextByUserId' , // 后端自定义API的名称。 params: { userId: $w .auth.currentUser.userId // 获取当前登录用户的唯一标识符 ( 平台用户ID ) 。 } , } ) ; if ( apiResult && apiResult.principal ) { // 成功获取校长上下文信息:将API返回的数据(包含校长、校区、机构信息)赋值给一个变量。 $w .app.dataset.state.currentPrincipal = apiResult ; // 【调试输出】确认全局变量已成功设置。 console.log ( "全局校长上下文已设置:" , $w .app.dataset.state.currentPrincipal ) ; $w .utils.hideLoading ( ) ; $w .utils.navigateTo ( { pageId: 'Campuses_jw_iy_list' , // 跳转的目标页面ID。 } ) ; } else { console.warn ( "当前用户不是校长或未找到相关信息:" , apiResult ? apiResult.message : '未知错误' ) ; // 使用Toast提示,告知用户操作失败或没有权限。 $w .utils.showToast ( { title: '失败' , // 提示标题。 icon: 'error' , // 提示图标为错误类型。 duration: 2000 // 提示显示2秒。 } ) ; } } catch ( error ) { console.error ( "获取校长信息失败:" , error ) ; // 使用Toast提示,告知用户系统出现问题。 $w .utils.showToast ( { title: '失败' , // 提示标题。 icon: 'error' , // 提示图标为错误类型。 duration: 2000 // 提示显示2秒。 } ) ; } finally { $w .utils.hideLoading ( ) ; } }
选中按钮,设置点击事件,调用我们的方法
第二部分:全局变量的应用与数据过滤——让数据为你而动!
一旦我们将当前校区的核心信息存储在全局变量中,后续所有涉及数据展示和操作的页面,都可以利用这些全局变量来自动过滤数据,实现“本校区数据隔离”的效果。
业务场景: 校长进入“校区管理”页面,只想查看并编辑他自己负责的校区信息。
选中数据表格组件,设置数据筛选 筛选条件设置为数据标识等于我们全局变量校区的数据标识
总结:掌控核心数据,赋能校区高效运营!
通过本篇教程,我们攻克了低代码平台中实现“本校区数据隔离”的核心挑战。我们学习了:
如何通过登录后的 引导页 ,进行用户身份的识别和校区上下文的加载。
如何巧妙地利用 全局变量 ,在整个应用中传递当前校区的关键信息。
如何在各个页面中应用 数据过滤 ,确保校长只查看和管理自己校区的专属数据。
现在,你的校长应用已经具备了强大的数据隔离能力,能够让校长更专注于本校区的运营,大大提升管理效率。
在下一篇教程中,我们将继续深入,探讨学员管理模块的搭建,让学员信息、学费、课时等数据也能在你的低代码教务系统中井然有序!
立即动手,让你的校长应用真正“活”起来吧!