引言:校长的困惑——我的数据在哪里?

亲爱的校长们,在上一篇教程中,我们一起打好了教务管理系统的数据“地基”,并初步搭建了机构管理员的全局视角。现在,让我们把目光聚焦到每一所教培机构的“心脏”——校区,以及校区的核心管理者——校长

你可能面临这样的疑问:
“我作为校长,只想管理我负责的那个校区的数据,低代码平台怎么知道哪个校区是我的?”
“我登录系统后,是不是能看到所有校区的数据,那我怎么分辨哪些是我该管的?”
“我的教务老师、招生老师,是不是也只能看到他们校区的数据?”

没错,这些是低代码平台在处理复杂权限和数据隔离时常遇到的“痛点”。由于低代码平台通常没有内置“当前用户所属校区”这种开箱即用的概念,我们需要通过一些巧妙的设计来实现数据的“专属感”。

本篇教程,我们将手把手带你解决这个核心问题,让校长登录后,看到的、管理的,都是他自己校区的专属数据!我们将围绕以下几个关键点展开:

  1. 登录后的“身份识别”:
     如何让系统知道“你就是XX校区的校长”。
  2. 全局变量的妙用:
     如何在应用中随时随地获取当前校区信息。
  3. 数据过滤的魔法:
     确保所有页面只显示本校区的数据。

让我们开始,为校长打造一个真正属于自己的“专属地盘”!

第一部分:登录后的“身份识别与校区上下文加载”——让系统认识你!

低代码平台通常会提供一个内置的用户登录功能,并能在用户登录后获取到该用户的唯一标识(我们称之为 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 functiongetPrincipalInfo(){
  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();
}
}


选中按钮,设置点击事件,调用我们的方法

第二部分:全局变量的应用与数据过滤——让数据为你而动!

一旦我们将当前校区的核心信息存储在全局变量中,后续所有涉及数据展示和操作的页面,都可以利用这些全局变量来自动过滤数据,实现“本校区数据隔离”的效果。

业务场景: 校长进入“校区管理”页面,只想查看并编辑他自己负责的校区信息。

选中数据表格组件,设置数据筛选

筛选条件设置为数据标识等于我们全局变量校区的数据标识

总结:掌控核心数据,赋能校区高效运营!

通过本篇教程,我们攻克了低代码平台中实现“本校区数据隔离”的核心挑战。我们学习了:

  • 如何通过登录后的引导页,进行用户身份的识别和校区上下文的加载。
  • 如何巧妙地利用全局变量,在整个应用中传递当前校区的关键信息。
  • 如何在各个页面中应用数据过滤,确保校长只查看和管理自己校区的专属数据。

现在,你的校长应用已经具备了强大的数据隔离能力,能够让校长更专注于本校区的运营,大大提升管理效率。

在下一篇教程中,我们将继续深入,探讨学员管理模块的搭建,让学员信息、学费、课时等数据也能在你的低代码教务系统中井然有序!

立即动手,让你的校长应用真正“活”起来吧!