You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在JavaScript文件中使用tal:conditions?上下文菜单开发疑问

问题:动态上下文菜单的权限控制与tal逻辑复用

我正在开发一款高可访问性的新上下文菜单,旧菜单在Index.html中通过固定ID结合tal:conditions实现权限和显示条件判断。现在新菜单需要为每个实例生成唯一ID,不再依赖固定的action与ID,但遇到了以下问题:

  • 尝试在tal条件内的<script>标签声明var emp_menu_items = [];,并从Employee类中移除该变量,仍收到"变量未定义"的错误
  • 想在Index.html中构建新菜单,但需要用到Employee.js内的参数,因此想知道有没有办法在JavaScript文件中复用tal:conditions的判断逻辑

相关代码片段

Index.html(旧菜单实现)

<a metal:fill-slot="context_menus" tal:omit-tag="">
    <ul id="employee_context_menu" class="hidden" tal:define="sec_posted python:container.util.checkSecurity(mn_name='Posted')">
      <li tal:condition="python:layout == 'np'"><a href="#assign_pat">Assign Patients</a></li>
      <li tal:condition="python:container.util.checkSecurity(mn_name='File')"><a href="#hr_file">HR File</a></li>
      <li tal:condition="python:sec_posted"><a href="#schedule">Pay Period Schedule</a></li>
      <li><a href="#Close">Close Menu</a></li>
    </ul>
</a>

Employee.js(旧菜单逻辑)

$name.contextMenu({
          menu:      "employee_context_menu",
          leftClick: true
        }, function(action, elem, position) {
          if(action == 'Close') {
            jQuery(".contextMenu").hide();
            return true;
          } else if(action === 'hr_file') {
            $iframe.attr('src', 'Personnel/cgi_utran/cgi_utran?args=-i'+$div.data('obj').id).load(function() {resizeIframe(this)});
          } else if(action === 'assign_pat') {

              $j.PatientAssignment.assign_check = emp.id;
              $name.addClass('highlight');

              var title_str ='Assign Patients to ' + employee_name;
              $j.PatientAssignment.sub_title = ''
              var mheight = $j(window).height()*0.7;
              $j('#beds').dialog('close');
              $j('#beds').css('max-height',mheight).dialog({
                      modal: false,
                      title: title_str,
                      height:'auto',
                      width: 'auto',
                      close: function() { $name.removeClass('highlight'); }
              });

              syncPatientAssignments();
              syncBedAssignments();
              return true;

          } else if(action === 'schedule') {
            $iframe.attr('src', 'ScheduleStaff/cgi_daystaff?szDate='+emp.shiftdate+'&amp;szID='+emp.id+'&amp;args=-t').load(function() {resizeIframe(this)});
          } else if(action === 'assign_work') {
            $iframe.attr('src', 'ScheduleStaff/cgi_daystaff?args=-t OneEmployee -I'+emp.id+' -B'+emp.shiftdate).load(function() {resizeIframe(this)});
          } else { }
          $name.addClass('highlight');
          $j("#modal_window").dialog({
            modal:    true,
            width:    'auto',
            show:     'scale',
            hide:     'scale',
            height:   'auto',
            close:  function() {window.location.reload(); $name.removeClass('highlight'); }
          });
        });

Employee.js(新菜单修改版本)

employeeContextMenu($div, emp, employee_name, count) {
      var emp_menu_items = [];
      var emp_menu = {};
      emp_menu_items.push({
          name: 'Assign Patients', title: 'Assign Patients',
          fn: function(el) {
            $j.PatientAssignment.assign_check = emp.id;
            var title_str ='Assign Patients to ' + employee_name;
            $j.PatientAssignment.sub_title = ''
            var mheight = $j(window).height()*0.7;

            if ($j('#beds').hasClass("ui-dialog-content") && $j( "#beds" ).dialog( "isOpen" )){
            $j('#beds').dialog('close');
              }
            $j('#beds').css('max-height',mheight).dialog({
                    modal: false,
                    title: title_str,
                    height:'auto',
                    width: 'auto',
                    position: [670, 115],
                    focus: function(event,ui) {
                      $j(".ui-icon-closethick").focus();
                    },
                    buttons: {
                      OK: function(){
                        $j(this).dialog("close");
                      }
                    }

            });

            syncPatientAssignments();
            syncBedAssignments();
            }
        });
      emp_menu_items.push({
            name: 'HR File', title: 'HR File',
            fn: function(el){return pop_over_window('Personnel/cgi_utran/cgi_utran?args=-i'+$div.data('obj').id);}});
      emp_menu_items.push({
            name: 'Pay Period Schedule', title: 'Pay Period Schedule',
            fn: function(el){return pop_over_window('ScheduleStaff/cgi_daystaff?szDate='+emp.shiftdate+'&amp;szID='+emp.id+'&amp;args=-t');}});
      emp_menu_items.push({
            name: 'Close Menu', title: '',
            fn: function(el){return false}, });
    
      emp_menu = new ContextMenu('employee_context_menu_'+count, 'Employee Context Menu Options', '.name', emp_menu_items, {container:$div[0]});
    }

解决方案

首先明确:tal:condition是服务器端模板渲染阶段执行的逻辑,JavaScript是客户端运行的代码,无法直接在JS文件中使用tal语法。之前的错误是因为当tal条件不满足时,对应的<script>标签不会被渲染,导致客户端某些场景下emp_menu_items未定义。

下面是三种可行的实现方案:

方案1:将权限判断结果注入DOM,在JS中读取

把tal的权限判断结果作为data-*属性绑定到员工元素上,在JS中读取这些属性来动态生成菜单项:

  1. 修改Index.html的员工元素:
<div class="employee-item" 
     data-layout-np="${layout == 'np'}"
     data-sec-file="${container.util.checkSecurity(mn_name='File')}"
     data-sec-posted="${container.util.checkSecurity(mn_name='Posted')}"
     <!-- 其他原有属性 -->
>
  <!-- 员工元素内容 -->
</div>
  1. 修改Employee.js的employeeContextMenu函数:
employeeContextMenu($div, emp, employee_name, count) {
  var emp_menu_items = [];
  
  // 从DOM读取权限标识(注意tal输出的布尔值可能是字符串形式的"True"/"False")
  const canAssignPat = $div.data('layout-np') === 'True';
  const canViewHrFile = $div.data('sec-file') === 'True';
  const canViewSchedule = $div.data('sec-posted') === 'True';

  // 根据权限动态添加菜单项
  if (canAssignPat) {
    emp_menu_items.push({
      name: 'Assign Patients', title: 'Assign Patients',
      fn: function(el) {
        // 原有Assign Patients逻辑...
      }
    });
  }

  if (canViewHrFile) {
    emp_menu_items.push({
      name: 'HR File', title: 'HR File',
      fn: function(el){return pop_over_window('Personnel/cgi_utran/cgi_utran?args=-i'+$div.data('obj').id);}
    });
  }

  if (canViewSchedule) {
    emp_menu_items.push({
      name: 'Pay Period Schedule', title: 'Pay Period Schedule',
      fn: function(el){return pop_over_window('ScheduleStaff/cgi_daystaff?szDate='+emp.shiftdate+'&amp;szID='+emp.id+'&amp;args=-t');}
    });
  }

  // 固定添加关闭菜单
  emp_menu_items.push({
    name: 'Close Menu', title: '',
    fn: function(el){return false},
  });

  emp_menu = new ContextMenu('employee_context_menu_'+count, 'Employee Context Menu Options', '.name', emp_menu_items, {container:$div[0]});
}

方案2:后端生成全局权限配置,注入客户端

在Index.html的tal模板中生成全局权限对象,供JS直接使用:

  1. 在Index.html中添加全局配置:
<script tal:omit-tag="">
  window.employeePermissions = {
    canAssignPat: ${layout == 'np'},
    canViewHrFile: ${container.util.checkSecurity(mn_name='File')},
    canViewSchedule: ${container.util.checkSecurity(mn_name='Posted')}
  };
</script>
  1. 在Employee.js中直接使用全局变量判断:
employeeContextMenu($div, emp, employee_name, count) {
  var emp_menu_items = [];
  
  if (window.employeePermissions.canAssignPat) {
    emp_menu_items.push({/* Assign Patients菜单项 */});
  }
  // 其余权限判断逻辑同上...
}

注意:如果页面存在多员工实例,需要确保全局配置是针对当前上下文的,或者改成每个元素单独的data属性方案。

方案3:tal模板生成菜单项配置,JS绑定逻辑

用tal生成菜单项的JSON结构,作为data属性存储,JS解析后绑定对应的函数:

  1. 修改Index.html的员工元素:
<div class="employee-item"
     data-menu-config='<tal:define="sec_posted python:container.util.checkSecurity(mn_name='Posted')">
       [
         <tal:if condition="python:layout == 'np'">{"name":"Assign Patients","title":"Assign Patients","action":"assign_pat"},</tal:if>
         <tal:if condition="python:container.util.checkSecurity(mn_name='File')">{"name":"HR File","title":"HR File","action":"hr_file"},</tal:if>
         <tal:if condition="python:sec_posted">{"name":"Pay Period Schedule","title":"Pay Period Schedule","action":"schedule"},</tal:if>
         {"name":"Close Menu","title":"","action":"close"}
       ]
     </tal:define>'>
</div>
  1. 修改Employee.js的employeeContextMenu函数:
employeeContextMenu($div, emp, employee_name, count) {
  // 解析tal生成的菜单配置
  const menuConfig = JSON.parse($div.data('menu-config'));
  
  var emp_menu_items = menuConfig.map(item => {
    // 根据action绑定对应的处理函数
    switch(item.action) {
      case 'assign_pat':
        return {
          ...item,
          fn: function(el) {
            // 原有Assign Patients逻辑...
          }
        };
      case 'hr_file':
        return {
          ...item,
          fn: function(el){return pop_over_window('Personnel/cgi_utran/cgi_utran?args=-i'+$div.data('obj').id);}
        };
      case 'schedule':
        return {
          ...item,
          fn: function(el){return pop_over_window('ScheduleStaff/cgi_daystaff?szDate='+emp.shiftdate+'&amp;szID='+emp.id+'&amp;args=-t');}
        };
      case 'close':
        return {
          ...item,
          fn: function(el){return false}
        };
      default:
        return item;
    }
  });

  emp_menu = new ContextMenu('employee_context_menu_'+count, 'Employee Context Menu Options', '.name', emp_menu_items, {container:$div[0]});
}

内容的提问来源于stack exchange,提问作者JThao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 14:39:15