development.md 42 KB

工具库功能和使用手册   牧语  配置型文档

mjs 开发型文档

宜搭工具库,宜搭 webApi 调用能力,集成钉钉 Api 接口能力,工具库,宜搭调用外部服务器能力,宜搭数据处理,...


前言

  • 组件赋值:文本和按钮显示修改需要使用 set("content", ""),修改按钮图标 set("iconName", ""), 赋值使用 setVal

  • 配置分支条件发起审批后,看到的预览是分之前的,只有当节点走到了分支才会显示。分支条件需要使用数字输入框和单选/下拉输入框,不支持单行收入、多行输入等

  • 若是在明细可通过 scope 获取到 state,执行 invokeValidate 方法验证表单,或是直接赋值操作. 若要取消报错提示, 可执行验证为 true 后, 再执行强制验证: forceValid() 可消失

  • 表单验证自定义规则入参有 3:value, state, ctx。在其规则函数内不能提供 scope 获取到明细组件值,若需要使用 scope 需要声明为方法

  • 人员搜索框组件 - 异步赋值人员搜索框,需要设置为对象类型,包含:label, key, avatar。 - 若是同步赋值人员搜索框,通过 employee 公式关联输入框,映射 userId 即可,若多个用集合

  • 明细赋值优化:兼容明细下拉框明细赋值更新异常问题: 让明细表单早于下拉框出现

  detailComp.setCurrentTargetDisable(true);
  detailComp.mergeVal(details);
  detailComp.setCurrentTargetDisable(false);
  • 组件的事件内, 入参可有 2: 1 个全局对象 ctx, 2 是当前组件的 fieldData:: 明细不支持, 没有值
  // 当明细个人业绩发生变更: 匹配结清情况
  export function onScopeResultChange(ctx, { value }) {

  }
  • 取值区别:cacheData 取缓存,下拉框 change 事件内取到的是上一次的值(代码赋值不会),建议使用 ctx.store.get("组件ID").getVal()

  • 接口公共参数处理

    • appType 可取 pageConfig.appType
    • userId 非管理员无权限,可使用宜搭公共管理员,yida_pub_account
  • 交互提示

    1. 弹出框
  ctx.fn.dialog({
    method: 'confirm', // 或confirm
    title: '当前是详情页编辑态',
  });
  1. 提示框::::: scope 不能使用 setBehavior, 可以使用 invokeValidate, 不能使用 forceValid
  // 冗余Toast提示方法
  function _showMessage(msg, type = "success") {
    if (!msg) return
    const ctx = window.LeGao.getContext()
    ctx.fn.toast({
      title: msg,
      type,
    });
  }
  • 判断当前流程页面位置
  const modeData = ctx.getInstanceData().flowData || {};
  // 提交态: 非 详情态 || 单据详情页编辑态 & 流程审批态
  if (!(modeData.viewMode || modeData.editMode)) return

  • 交互提示

    1. 弹出框
  ctx.fn.dialog({
    method: 'confirm', // 或confirm
    title: '当前是详情页编辑态',
  });
  1. 提示框::::: scope 不能使用 setBehavior, 不能使用 invokeValidate, 不能使用 forceValid
  // 冗余Toast提示方法
  function _showMessage(msg, type = "success") {
    if (!msg) return
    const ctx = window.LeGao.getContext()
    ctx.fn.toast({
      title: msg,
      type,
    });
  }
  • 高级公式

    • 使用高级公式,条件如涉及到当期表,则条件的首参必须是目标表字段:否则报:主条件参数错误
    • 高级公式 crud:可执行明细插入明细,在关联审批流时。不能同时操作多个明细,或明细和明细外组件
  • 常用公式

    • 由时间戳生成编号 CONCATENATE("PRO",LEFT(TIMESTAMP(SYSTIME()),10)), CONCATENATE("COC",TEXT(TODAY(),"yyyyMMddHHmmSS"))
    • 日期组件默认值:TIMESTAMP(SYSTIME())
    • 映射人员搜索框人员信息: USERFIELD(人员搜索框,"userId") -- name; 获取部门: DEPTNAME(人员搜索框)

--

集成

  • 前言:使用工具库通过 mjs 对象进行调用,若需要在加载即调用方法,请在 _initTaskCount 调用

  • 引入:绑定宜搭 didMount 事件,调用 loadAliworkSDK(ctx) 方法加载依赖

  • 说明:宜搭支持 es6 async await 语法。工具库所有有返回值的方法都会返回 Promise

//---------------------- common ----------------------//

// 工具库
function loadAliworkSDK(ctx) {
  const src = "https://aliwork.zitoo.com.cn/mjs.min.js";
  const script = document.createElement('script');
  script.setAttribute('type', 'text/javascript');
  script.setAttribute("src", src);
  document.body.appendChild(script);
  script.onload = () => _mjsOnload(ctx);
}
loadAliworkSDK(window.LeGao.getContext())

//---------------------- private ----------------------//

// 加载即调用方法请在此处进行调用
function _mjsOnload(ctx) {
	// ....
}

//---------------------- event ----------------------//

// 页面加载,载入工具库
export function didMount(ctx) {
  //loadAliworkSDK(ctx)
}
  • 明细查询标准接口和代码
//---------------------- common ----------------------//

// 工具库
function loadAliworkSDK(ctx) {
  const src = "https://aliwork.zitoo.com.cn/sampleEnginnering/mjs.min.js";
  const script = document.createElement('script');
  script.setAttribute('type', 'text/javascript');
  script.setAttribute("src", src);
  document.body.appendChild(script);
  script.onload = () => _mjsOnload(ctx);
}
loadAliworkSDK(window.LeGao.getContext())  // 载入工具库

//---------------------- private ----------------------//

// 加载即调用方法请在此处进行调用
async function _mjsOnload(ctx) {
  // ...
}

// 重置明细数据::当前明细组件Id,明细组件对照表Id集合
function _resetDetailData(ctx) {
  const compDetail = ctx.store.get("tableField_kewa74uk")
  const details = compDetail.getData().fieldData.value
  details.forEach(row => propProduction.forEach(prop => row[prop.cur] = { fieldData: { value: "" } }))
  compDetail.mergeVal(details)
}

// 查询关联的合同表单的产品明细::当前明细组件Id,过滤条件,来源表Id,下拉框组件对象,来源表明细Id,明细组件对照表Id集合
async function _queryContractDetail(ctx, condition, comp) {
  const compDetail = ctx.store.get("tableField_kewa74uk")
  compDetail.setCurrentTargetDisable(true);
  // 只能查询当前formId下的表单字段
  const conditionList = [{ fieldName: "selectField_kf0jdry4", value: condition }]
  const appType = pageConfig.appType
  const userId = loginUser.userId
  const res = await mjs.aliworkTheDynamicSubsidiary("/form/select", {
    appType,
    userId,
    formId: 'FORM-YSA66KC16Z5JALSJWT97I0WZHXRU2CYGUGWEKC',
    conditionList
  }).catch(() => [])
  // 容错 && 提示
  const isErr = res.list.length
  comp.invokeValidate(isErr, "合同数据查询失败")
  comp.forceValid()
  if (!isErr) return
  // 更新明细
  const form = res.list[0].formData
  const details = form["tableField_kewa74uk"].map(detail => {
    return propProduction.reduce((row, prop) => {
      const value = prop.src ? detail[prop.src] : ""
      row[prop.cur] = {
        fieldData: { value }
      }
      return row
    }, {})
  })
  setTimeout(() => compDetail.mergeVal(details))  // 赋值不能及时更新
  compDetail.setCurrentTargetDisable(false);
}

//---------------------- source ----------------------//

const propProduction = [{
  // 产品名称
  src: "selectField_kewa74uo",
  cur: "selectField_kewa74uo"
}]

//---------------------- event ----------------------//

// 页面节点加载渲染完毕
export function didMount(ctx) {
  // ...
}

// 当合同编号发生变更
export function onContractChange(ctx) {
  _resetDetailData(ctx)
  const comp = ctx.store.get("selectField_kf0n0xsl")
  const condition = comp.getData().fieldData.value.split("-")[0]
  if (condition) _queryContractDetail(ctx, condition, comp)
}
  • 判断当前流程页面位置
  // 提交态: 非 详情态 || 单据详情页编辑态 & 流程审批态
  const modeData = ctx.getInstanceData().flowData || {};
  if (!(modeData.viewMode || modeData.editMode)) onStaffChange(ctx, loginUser.userId)

钉钉 Api

钉钉 JSAPI 调试页面PC 端可能无法使用,钉钉打开宜搭默认会跳转外部浏览器

所有钉钉接口都支持集成 ⇨ 弹出框,设备信息,日历时间选择,导航,开启刷新效果,扫一扫,存储,地图,电话,发钉消息,通讯录选人,釘盘,音视频接口,视频会议,支付,屏幕旋转,....

  1. 显示加载 loading: mjs.showLoading(). 不会自动关闭, 请在合适地方调用关闭, 或通过定时器完成 setTimeout(() => mjs.hideLoading(), 2000)
  2. 关闭加载 loading: mjs.hideLoading()
  3. 显示提示 Toast
    1. 入参详见
    2. mjs.showMessageToast(). 默认 2s 后消失,若持续可见,传入对象属性 duration 为 0 即可;message 为空会被忽略
  4. 获取当前位置信息:mjs.getLocation()

--

工具库

工具库

  1. 获取随机数: getRandomNumber(count = 1, max = 999, min = 1)
  2. 导入明细组件: 在 _mjsOnload 中调用: 上传组件 id 如 fileReader, 读取上传模板的 sheet 名称 如 Sheet1, 得到格式化后数据的回调, 设置明细数据源
   mjs.createElementUpload("fileReader", "Sheet1", function (details) {
     // 数据回调更新当前明细组件
     ctx.store.get('tableField_kaxke4k9').mergeVal(details);
   })

3.货币精确计算:const clac = mjs.calcLib(), 使用详见

  1. 时间格式化
  2. 获取汉字首字符 getCaptureUpper

  3. 公式

  • 避免单号重复,公式可用 yyyyMMddHHmmssSS,若太长,可用 yyMMddHHmmss

--

宜搭 Api

  1. 关于下拉组件

    1. 关于下拉单选:在非明细内,通过数据关联或代码赋值,即使未匹配到下拉选项,可以正常显示(明细内必须命中下拉选项):: 若需要能匹配, 提前创建组件占位, 可配合明细组件可用状态属性使用
    2. 关于下拉多选:下拉多选不支持默认值数据联动,只能联动其下拉选项,联动结果为下拉选项是上一层选择的值。默认值可通过代码复制,无论是否在明细组件内,都不需要命中选项直接赋值
  2. 关于人员搜索框: 若多选使用集合即可

    1. 同步:人员框通过 employee 关联输入框, 在 didMount 赋值 userId,即在公式响应前赋值
    2. 异步:因宜搭没有获取主管公式,通过钉钉调用后,写入人员搜索框,数据类型符合组件格式
  3. 将宜搭添加到微应用入口的说明:

    1. 在地址后添加 &corpid=dingebe19bcf228f85ec35c2f4657eb6378f&dd_addcookie=true, 实现钉钉跳转宜搭免登
    2. 添加 dd_addcookie=true 后宜搭默认通过外部浏览器打开, 只保留 corpId 也能实现免登, 且 PC 端在钉钉内部打开: 可能宜搭对钉钉的适配有些小问题
    3. 若存在重定向, 需要在 didMount 处理, 否则免登会失效
  4. 调用网络请求前, 需要 appTypeuserId

    1. 关于 userId: 若页面不显示人员, 可通过人员选择框或输入框组件绑定当前人员公式, 放到一个隐藏的容器中获取 (该组件隐藏后不能后不能获取其值): 若是动态选择需要 setTimeout 来获取
    2. 关于 appType: 即当前应用的 Id, 配置在 DataPool 数据池 并全页面共享. 通过 const appType = ctx.dp.getValue("appType") 获取. 也可通过上传组件获取, 该组件可直接隐藏
    3. 关于 setBehavior, 代码设置优先级高于权限配置, 因此若设置了 NORMAL, 则都会可见. 按钮不支持配置状态, 只要配置了就会在数据页面上展示
   // 加载即调用方法请在此处进行调用
   function _mjsOnload(ctx) {
    // 配置单据数据初始化
    const queryForm = async function (userId) {
        // ... 私有且页面加载即发生的请求
    }
    // 页面切换输入框会丢失,若存在userd,无需定时器延迟兼容,提升加载速度
    const userId = sessionStorage.getItem("USER_ID")
    if (userId) {
      queryForm(userId)
    } else {
      setTimeout(() => {
        // 页面切换输入框会丢失,若存在userd,无需定时器延迟兼容,提升加载速度
        const state = ctx.store.get('employeeField_k9u4zopg')
        userId = state.cacheData.fieldData.value[0].key
        sessionStorage.setItem("USER_ID", userId)
        queryForm(userId)
      }, 200)
    }
   }
  1. 数据查询不能使用文本框::偶现,可能是页面通过 schema 导入所致,通过下拉框映射编码可以查询到(下拉框赋值不知道默认值,赋值延迟 1s 处理,页面加载公式关联 Id 为空)
   // 下拉框赋值不知道默认值,赋值延迟 1s 处理,页面加载公式关联 Id 为空: 数据查询不能使用文本框::偶现,可能是页面通过 schema 导入所致
   setTimeout(() => {
     const code = ctx.store.getData("textField_kccqtl25").fieldData.value
     ctx.store.get("selectField_kf0jdry4").setVal(code)
   }, 1000)
  1. 动态设置明细组件
   // 采购明细请求数据
   function _changePurchaseDetail(ctx, count) {
    const detail = {
    textField_k9mi4iny: {  // 当前明细组件下子组件compId
        fieldData: {
          value: '物料001',
        }
      },
      textField_ka3ajelw: {
        fieldData: {
          value: "称重",
        }
      },
      numberField_k9mi4io0: {
        fieldData: {
          value: "300",
        }
      },
      textField_k9mi4inx: {
        fieldData: {
          value: "千克",
        }
      },
      numberField_k9mi4inz: {
        fieldData: {
          value: 10,
        }
      }
    };
    // 明细赋值优化
    const detailComp = ctx.store.get('tableField_ka3ajelv');
    detailComp.setCurrentTargetDisable(true);
    detailComp.mergeVal(Array(Number(count)).fill(detail));
    detailComp.setCurrentTargetDisable(false);
   }
  1. 明细赋值优化:兼容明细下拉框明细赋值更新异常问题: 让明细表单早于下拉框出现
   // 明细赋值优化:兼容明细下拉框明细赋值更新异常问题: 让明细表单早于下拉框出现
   function _optimizeDetailComponent(state, details, count = 10) {
   if (!state) return
    if (!details) {
      // 明细组件赋值优化
      state.setCurrentTargetDisable(true);
      state.mergeVal(Array(count).fill({}));
    } else {
      state.mergeVal(details);
      state.setCurrentTargetDisable(false);
   }
   }
  1. 动态设置下拉框组件: 下拉框 value 不支持复杂的数据类型. 但对象支持添加属性 - 「如意多部门切换关联审批人」
   // 采购申请请求数据
   function _loadPurchaseApply (ctx) {
    const options = [
      {
        text: '项目 004',
        value: 4,
      },
      {
        text: '项目 001',
        value: 1
      },
      {
        text: '项目 007',
        value: 7
      }
    ]
    ctx.store.get('selectField_k9mi4inr').set('options', options);
   }
  1. 设置数据源和行为
   // 删除测试
   export function onClickDelete(ctx) {
     console.log("获取当前页面的唯一标识,即formUuid.", ctx.getCurrentPageId())
     console.log("获取当前页面的数据池(DataPool).", ctx.getDataPool())
     console.log("根据组件的fieldId,返回实际页面上的组件实例引用.", ctx.getComponentInstance("button_ka3ajelx"))
     // 隐藏操作:不支持直接删除
     ctx.store.get('button_ka3ajelx').set('behavior', { fieldBehavior: 'HIDDEN' })
     const state = ctx.store.get('selectField_k9mi4ins')
     state.setVal('') // 清空隐藏
     state.set('behavior', { fieldBehavior: 'HIDDEN' })
     ctx.store.get('selectField_k9mi4inq').setVal('请选择项目')
   }
  1. 采购订单案例:

    1. 关联关系: 采购申请 项目字段 关联项目表单中的 项目名称和项目编号, 采购订单的 项目名称 关联采购申请中的 项目名称.
    2. 操作项目: 当选择项目名称下拉框, 后台查询所选项目下的所有采购申请(未做审批过滤). 查询采购将采购申请下拉框置为 NORMAL 可用状态, 并更新采购申请下拉框以及其对应明细数据
    3. 操作采购: 当选择采购申请下拉框时, 在 change 事件内, 将其明细数据填充到明细组件. 项目修改, 清除, 或采购清除重置采购下拉框和明细数据
    4. 备注说明:
      1. 表单关联仅会关联选择字段, 且会和其 组件Id绑定, 组件删改导致 Id 变更需要维护数据源
      2. 查询支持过滤条件, 条件和组件 id 要进行绑定, 且只能查询当前 formId 下的表单字段
    5. 使用说明: 将 event 分割线下事件进行绑定, 将页面中对应的 组件Id 更新. 宜搭是表单的值是通过 组件Id绑定, 取关联表单在原表单拷贝最合适
    //---------------------- common ----------------------//
    
    // 工具库
    function loadAliworkSDK(ctx) {
      const src = "https://aliwork.zitoo.com.cn/sampleEnginnering/mjs.min.js";
      const script = document.createElement('script');
      script.setAttribute('type', 'text/javascript');
      script.setAttribute("src", src);
      document.body.appendChild(script);
      script.onload = () => _mjsOnload(ctx);
    }
    
    //---------------------- private ----------------------//
    
    // 查询采购申请表单关联的明细
    async function _queryDetail(ctx, condition) {
      // 只能查询当前formId下的表单字段
      const conditionList = [{ fieldName: "selectField_k9tw8gaq", value: condition } ]
      const appType = ctx.dp.getValue("appType") // 全局配置在数据池,避免通过上传组件获取
      const state = ctx.store.get('employeeField_kaotctsb')
      const userId = state.cacheData.fieldData.value[0].key
      // 查询采购申请表单关联的明细
      const res = await mjs.aliworkTheDynamicSubsidiary("/form/select", {
        appType,
        userId,
        formId: 'FORM-RM966M919ODFEKPD5USBJBKG4R3022T98WT9K9G4',
        conditionList
      })
      // 设置采购申请下拉框可用,并设置下拉数据源
      ctx.store.get('selectField_k9mi4inr').set('behavior', { fieldBehavior: 'NORMAL' })
      const options = res.list.map((item, index) => {
        const form = item.formData
        const select = form["textField_kaorkyrr"] + '-' + form["textField_kaoqmj6i"]
        const details = form["tableField_k9tw8gar"].map(detail => {
          return {
            textField_k9mi4iny: {
              fieldData: {
                value: detail["selectField_k9xury38"],
              }
            },
            textField_kaq54olm: {
              fieldData: {
                value: detail["textField_k9tw8gat"],
              }
            },
            numberField_k9mi4io0: {
              fieldData: {
                value: undefined,
              }
            },
            numberField_k9mi4inz: {
              fieldData: {
                value: detail["numberField_k9tw8gav"],
              }
            },
            textField_k9mi4inx: {
              fieldData: {
                value: detail["textField_k9xury39"],
              }
            }
          }
        })
        return {
          text: select,
          value: index, // 下拉框value不支持复杂的数据类型:但支持添加属性
          details: details
        }
      })
      ctx.store.get('selectField_k9mi4inr').set('options', options);
    }
    
    // 重置选择状态和数据
    function _resetPurchaseAndDetail(ctx, clearOptions) {
      ctx.store.get('tableField_kaq54oll').mergeVal([]);
      const state = ctx.store.get('selectField_k9mi4inr')
      state.setVal("")
      if (clearOptions) state.set('options', []);
    }
    
    //---------------------- event ----------------------//
    
    // 页面加载,载入工具库
    export function didMount(ctx) {
      loadAliworkSDK(ctx)
    }
    
    // 项目下拉框:关联采购申请表单,选择设置采购申请下拉框可用,并处理数据
    export function onProjectChange(ctx) {
      _resetPurchaseAndDetail(ctx, true)
      const selected = ctx.store.getData("selectField_k9mi4inq").fieldData.value
      if (selected) _queryDetail(ctx, selected)
      else ctx.store.get('selectField_k9mi4inr').set('behavior', { fieldBehavior: 'DISABLED' })
    }
    
    // 采购申请下拉框:将格式好数据设置到明细
    export function onPurchaseChange(ctx) {
      const state = ctx.store.getData("selectField_k9mi4inr")
      if (!state.fieldData.value) {
        _resetPurchaseAndDetail(ctx)
        return
      }
      const details = state.options[state.fieldData.value].details
      ctx.store.get('tableField_kaq54oll').mergeVal(details);
    }
    
  2. 采购入库: 关联采购订单表单, 逻辑代码镜像 5. 采购订单案例

    //---------------------- common ----------------------//
    
    // 工具库
    function loadAliworkSDK(ctx) {
      const src = "https://aliwork.zitoo.com.cn/sampleEnginnering/mjs.min.js";
      const script = document.createElement('script');
      script.setAttribute('type', 'text/javascript');
      script.setAttribute("src", src);
      document.body.appendChild(script);
      script.onload = () => _initTaskCount(ctx);
    }
    
    //---------------------- private ----------------------//
    
    // 加载即调用方法请在此处进行调用
    async function _initTaskCount(ctx) {
      // ....
      console.log("mjs", mjs)
    }
    
    // 查询采购申请表单关联的明细
    async function _queryDetail(ctx, condition) {
      // 只能查询当前formId下的表单字段
      const conditionList = [{ fieldName: "selectField_k9tw8gaq", value: condition }]
      const appType = ctx.dp.getValue("appType") // 全局配置在数据池,避免通过上传组件获取
      const state = ctx.store.get('employeeField_k9rsqfip')
      const userId = state.cacheData.fieldData.value[0].key
      // 查询采购申请表单关联的明细
      const res = await mjs.aliworkTheDynamicSubsidiary("/form/select", {
        appType,
        userId,
        formId: 'FORM-W5666U816ODFAUQBY4R7VZAJJL9T1KSD4IM9K151',
        conditionList
      })
      // 设置采购申请下拉框可用,并设置下拉数据源
      ctx.store.get('selectField_k9mi4inr').set('behavior', { fieldBehavior: 'NORMAL' })
      const options = res.list.map((item, index) => {
        const form = item.formData
        const select = form["employeeField_kaotctsb"] + "-" + form["textField_kaqfkxm5"]
        const details = form["tableField_kaq54oll"].map(detail => {
          return {
            textField_kaqd9y6k: {
              fieldData: {
                value: detail["textField_k9mi4iny"],
              }
            },
            textField_kaqe45de: {
              fieldData: {
                value: detail["textField_kaq54olm"],
              }
            },
            textField_kaqd9y6o: {
              fieldData: {
                value: detail["numberField_k9mi4io0"],
              }
            },
            numberField_kaqd9y6l: {
              fieldData: {
                value: detail["numberField_k9mi4inz"],
              }
            },
            textField_kaqd9y6m: {
              fieldData: {
                value: detail["textField_k9mi4inx"],
              }
            }
          }
        })
        return {
          text: select,
          value: index, // 下拉框value不支持复杂的数据类型
          details: details,
          supplier: form["selectField_k9mi4ins"] // 供应商
        }
      })
      ctx.store.get('selectField_k9mi4inr').set('options', options);
    }
    
    // 重置选择状态和数据
    function _resetPurchaseAndDetail(ctx, clearOptions) {
      ctx.store.get('tableField_kaqd9y6n').mergeVal([]);
      const state = ctx.store.get('selectField_k9mi4inr')
      state.setVal("")
      if (clearOptions) state.set('options', []);
    }
    
    //---------------------- event ----------------------//
    
    // 页面加载,载入工具库
    export function didMount(ctx) {
      loadAliworkSDK(ctx)
    }
    
    // 项目下拉框:关联采购申请表单,选择设置采购申请下拉框可用,并处理数据
    export function onProjectChange(ctx) {
      _resetPurchaseAndDetail(ctx, true)
      const selected = ctx.store.getData("selectField_k9mi4inq").fieldData.value
      if (selected) _queryDetail(ctx, selected)
      else ctx.store.get('selectField_k9mi4inr').set('behavior', { fieldBehavior: 'DISABLED' })
    }
    
    // 采购申请下拉框:将格式好数据设置到明细
    export function onPurchaseChange(ctx) {
      const state = ctx.store.getData("selectField_k9mi4inr")
      if (!state.fieldData.value) {
        _resetPurchaseAndDetail(ctx)
        return
      }
      const option = state.options[state.fieldData.value]
      ctx.store.get('selectField_k9mi4ins').setVal(option.supplier || "")
      ctx.store.get('tableField_kaqd9y6n').mergeVal(option.details);
    }
    
  3.  通过 Excel 导入明细组件

    //---------------------- common ----------------------//
    
    // 工具库
    function loadAliworkSDK(ctx) {
      const src = "https://aliwork.zitoo.com.cn/mjs.min.js";
      const script = document.createElement('script');
      script.setAttribute('type', 'text/javascript');
      script.setAttribute("src", src);
      document.body.appendChild(script);
      script.onload = () => _initTaskCount(ctx);
    }
    
    //---------------------- private ----------------------//
    
    // 加载即调用方法请在此处进行调用
    async function _initTaskCount(ctx) {
      // ....
      mjs.createElementUpload("fileReader", "Sheet1", function (details) {
        // 数据回调更新当前明细组件
        console.log(details)
        ctx.store.get('tableField_kaxke4k9').mergeVal(details);
        console.log("detail Id", ctx.store.get('tableField_kaxke4k9'))
      })
    }
    
    //---------------------- event ----------------------//
    
    // 页面节点加载渲染完毕
    export function didMount(ctx) {
      loadAliworkSDK(ctx)
    }
    
    // 响应Excel上传事件
    export function onUploadClick(ctx) {
      document.getElementById('fileReader').click();
    }
    
  4. 更新明细组件宽度: 传入组件 class .node_k9z1uvrr, 在明细组件标签上; 变更宽度建议用百分比, 大于 100%

    // 设置明细组件宽度: 将明细拖入容器,设置其overflow scroll
    function setDetaiComplWidth(className, width) {
      // setDetaiComplWidth(".node_k9z1uvrr", "130%") 明细组件的唯一id
      const elem = document.querySelector(className);
      if (!elem) return
      elem.style['max-width'] = width
      elem.style.width = width
    }
    
  5. 获取钉钉主管列表接口, 兼容宜搭数据关联: 人员搜索框不能得到其值, 通过公式关联到输入框后读取输入框

    //---------------------- common ----------------------//
    
    // 工具库
    function loadAliworkSDK(ctx) {
      const src = "https://aliwork.zitoo.com.cn/ruyi/mjs.min.js";
      const script = document.createElement('script');
      script.setAttribute('type', 'text/javascript');
      script.setAttribute("src", src);
      document.body.appendChild(script);
      script.onload = () => _mjsOnload(ctx);
    }
    
    //---------------------- private ----------------------//
    
    // 加载即调用方法请在此处进行调用
    function _mjsOnload(ctx) {
      // ....
    }
    
    //---------------------- event ----------------------//
    
    // 页面加载,载入工具库
    export function didMount(ctx) {
      loadAliworkSDK(ctx)
    }
    
    // 报销人变更
    let userIdT = ''
    export function onStaffChange(ctx) {
      // 清空历史数据
      const deptComp = ctx.store.get('selectField_kc3ixvdp')
      deptComp.setVal("");
      deptComp.set('options', []);
      deptComp.setBehavior("DISABLED")
      ctx.store.get("employeeField_kbrl75yp").setVal([])
      ctx.store.get("employeeField_kbrp6uv1").setVal([])
      // 拉取报销人对应主管列表
      const getManager = async (userId) => {
        const res = await mjs.ddingAddressBook(userId);
        const obj = JSON.parse(res.data);
        // 部门列表
        const options = []
        const departs = Object.keys(obj.deptInfo)
        departs.forEach((key, index) => {
          // 遍历审批人
          const arr = []
          obj.parentDpData[key].forEach((item) => {
            if (!item || item.userid === userId) return true // 空 || 当前部门主管
            if (!item.errcode) arr.push({
              avatar: item.avatar,
              key: item.userid,
              label: `(${item.name})${item.userid}`
            });
          });
          // 报销人所属部门单选选项预设
          options.push({
            text: obj.deptInfo[key],
            value: index,
            approve: arr,
          })
          if (arr.length) deptComp.setBehavior("NORMAL")
        })
        // 若仅属于一个部门直接赋值
        deptComp.set('options', options);
        if (options.length === 1 && options[0].approve.length) {
          deptComp.setVal(0);
          onDepartChange(ctx)
        }
      }
      let timer = setInterval(() => {
         const userId = ctx.store.get('textField_kbrktjhc').cacheData.fieldData.value
         // 宜搭组件关联不支持置空,添加变量记录
         if (userId && userId !== userIdT) {
           userIdT = userId
           clearInterval(timer)
           timer = null
           getManager(userId)
         }
       }, 100)
     }
    
     // 当报销人所属部门修改后
     export function onDepartChange(ctx) {
       const state = ctx.store.get("selectField_kc3ixvdp").getData()
       const arr = state.options[Number(state.fieldData.value)].approve
       // 分管董事
       if (arr.length) ctx.store.get("employeeField_kbrp6uv1").setVal([arr.pop()])
       // 上级主管列表
       ctx.store.get("employeeField_kbrl75yp").setVal(arr)
     }
    
  6. 明细组件关联状态, 绑定到审批流程配置内

    // 当流程变更:需要董事审批
    export function onProcessChange(ctx) {
      const details = ctx.store.getData("tableField_k9z1uvs2").fieldData.value
      const condition = {'公关': -1, "年检/软件服务费/律师费等/质量检测费": -1, "门店试吃费/新品研发": 300, "劳保用品": 500, "召开会议发生的场地餐费住宿费等": 500, "快递费/办公用品": 500, "办事处固话宽带": 500,
        "打车(出差发生除外)": 40, "业务招待费": -1, "消防材料/小型工具器具/直营店自购辅料": 500, "直营店税金": -1, "财产保险/雇主责任险/食品安全险": 1000, "劳务中介费/招聘平台": 1000, "垃圾清理保洁/花木租赁/绿化费/泔水处理": 500,
        "书籍资料/继续教育/新员工培训": 500, "奖金": -1, "食堂餐费": -1, "厂房修缮": 500, "日常修理费": 500, "视频拍摄/百度竞价/搜索引擎/网络推广": 1000, "总部-宣传物料/外卖平台补贴": -1, "团建/节日发放福利/加班餐费/员工探视": -1}
      const approve = Object.keys(condition)
      details.forEach(item => {
        const pro = item["selectField_k9z1uvsq"].fieldData.value
        const amm = mjs.calcLib().times(Number(item["textField_k9m12fzg"].fieldData.value), Number(item["numberField_k9klpsw2"].fieldData.value)) // 计算金额
        // 是否需要一级主管审批状态值
        const app = (approve.includes(pro) && amm > condition[pro])
        item["numberField_kbyu00rh"] = { fieldData: { value: Number(app) } } // 重置状态添加响应式
      })
      ctx.store.get("tableField_k9z1uvs2").mergeVal(details)
    }
    
  7. 如意代报销流程

    //---------------------- common ----------------------//
    
    // 工具库
    function loadAliworkSDK(ctx) {
      const src = "https://aliwork.zitoo.com.cn/ruyi/mjs.min.js";
      const script = document.createElement('script');
      script.setAttribute('type', 'text/javascript');
      script.setAttribute("src", src);
      document.body.appendChild(script);
      script.onload = () => _initTaskCount(ctx);
    }
    
    //---------------------- private ----------------------//
    
    // 加载即调用方法请在此处进行调用
    function _initTaskCount(ctx) {
      onStaffChange(ctx, loginUser.userId)
    }
    
    //---------------------- event ----------------------//
    
    // 页面加载,载入工具库
    export function didMount(ctx) {
      loadAliworkSDK(ctx)
    }
    
    // 报销人变更
    let userIdT = ''
    export function onStaffChange(ctx, userId) {
      // 清空历史数据
      const deptComp = ctx.store.get('selectField_kc3ixvdp')
      if (deptComp.getData().fieldData.value) deptComp.setVal("");
      deptComp.set('options', []);
      ctx.store.get("employeeField_kbrl75yp").setVal([])
      ctx.store.get("employeeField_kbrp6uv1").setVal([])
      // 拉取报销人对应主管列表
      const getManager = async (userId) => {
        const res = await mjs.ddingAddressBook(userId);
        const obj = JSON.parse(res.data);
        // 部门列表
        const options = []
        const departs = Object.keys(obj.deptInfo)
        departs.forEach((key, index) => {
          // 遍历审批人
          const arr = []
          obj.parentDpData[key].forEach((item) => {
            if (!item || item.userid === userId) return true // 空 || 当前部门主管
            if (!item.errcode) arr.push({
              avatar: item.avatar,
              key: item.userid,
              label: item.name
            });
          });
          // 报销人所属部门单选选项预设
          const name = obj.deptInfo[key]
          options.push({
            text: name,
            value: name,
            approve: arr,
          })
        })
        // 若仅属于一个部门直接赋值
        deptComp.set('options', options);
        if (options.length === 1 && options[0].approve.length) {
          deptComp.setVal(options[0].value);
          onDepartChange(ctx)
        }
      }
      if (!userId) userId = ctx.store.getData("employeeField_kc8z8awp"),fieldData.value[0].key
      getManager(userId)
    }
    
    // 当报销人所属部门修改后
    export function onDepartChange(ctx) {
      const state = ctx.store.get("selectField_kc3ixvdp").getData()
      const arr = (state.options.find(item => state.fieldData.value == item.value)).approve
      // 分管董事
      if (arr.length) ctx.store.get("employeeField_kbrp6uv1").setVal([arr.pop()])
      // 上级主管列表
      ctx.store.get("employeeField_kbrl75yp").setVal(arr)
    }
    
  8. ...

//---------------------- common ----------------------//

// 工具库
function loadAliworkSDK(ctx) {
  const src = "https://aliwork.zitoo.com.cn/sampleEnginnering/mjs.min.js";
  const script = document.createElement('script');
  script.setAttribute('type', 'text/javascript');
  script.setAttribute("src", src);
  document.body.appendChild(script);
  script.onload = () => _mjsOnload(ctx);
}
loadAliworkSDK(window.LeGao.getContext())  // 载入工具库

//---------------------- private ----------------------//

// 加载即调用方法请在此处进行调用
async function _mjsOnload(ctx) {
  // ...
}

// 设置明细组件宽度: 将明细拖入容器,设置其overflow scroll
function setDetaiComplWidth(className, width) {
  // setDetaiComplWidth(".node_k9z1uvrr", "130%") 明细组件的唯一id
  const elem = document.querySelector(className);
  if (!elem) return
  elem.style['max-width'] = width
  elem.style.width = width
}

// 重置明细数据::当前明细组件Id,明细组件对照表Id集合
function _resetDetailData(ctx) {
  const compDetail = ctx.store.get("tableField_kewa74uk")
  const details = compDetail.getData().fieldData.value
  details.forEach(row => propProduction.forEach(prop => row[prop.cur] = { fieldData: { value: prop.def } }))
  compDetail.mergeVal(details)
}

// 查询关联的合同表单的产品明细::当前明细组件Id,过滤条件,来源表Id,下拉框组件对象,来源表明细Id,明细组件对照表Id集合
async function _queryContractDetail(ctx, condition, comp) {
  const compDetail = ctx.store.get("tableField_kewa74uk")
  compDetail.setCurrentTargetDisable(true);
  // 只能查询当前formId下的表单字段
  const conditionList = [{ fieldName: "textField_kf0n0xsh", value: condition }]
  const appType = pageConfig.appType
  const userId = loginUser.userId
  const res = await mjs.aliworkTheDynamicSubsidiary("/form/select", {
    appType,
    userId,
    formId: 'FORM-YSA66KC1626J1EYY0FUWD9JXGNRL2SM0CHWEK23',
    conditionList
  }).catch(() => [])
  // 容错 && 提示
  const isErr = res.list.length
  comp.invokeValidate(isErr, "采购计划查询失败")
  comp.forceValid()
  if (!isErr) return
  // 更新明细
  const form = res.list[0].formData
  const details = form["tableField_kewa74uk"].map(detail => {
    return propProduction.reduce((row, prop) => {
      const value = prop.src ? detail[prop.src] : prop.def
      row[prop.cur] = {
        fieldData: { value }
      }
      return row
    }, {})
  })
  setTimeout(() => compDetail.mergeVal(details))  // 赋值不能及时更新
  compDetail.setCurrentTargetDisable(false);
  DETAILS_PRODUCTION = details  // 记录供应商修改缓存数据
}

//---------------------- source ----------------------//

const propProduction = [{
  // 产品站点
  src: "selectField_kf57ylzr",
  cur: "selectField_kf57ylzr",
  def: ""
},
{
  // 产品站点编号
  src: "textField_kewa74uq",
  cur: "textField_kewa74uq"
},
{
  // 产品模块
  src: "selectField_kewa74uo",
  cur: "selectField_kewa74uo"
},
{
  // 产品线
  src: "selectField_kewa74up",
  cur: "selectField_kewa74up"
},
{
  // 产品类型
  src: "selectField_kf54yfkg",
  cur: "selectField_kf54yfkg"
},
{
  // 规格
  src: "textField_kewa74ur",
  cur: "textField_kewa74ur"
},
{
  // 标准报价
  src: "numberField_kewa74us",
  cur: "numberField_kewa74us"
},
{
  // 折扣
  src: "numberField_kewa74ut",
  cur: "numberField_kewa74ut"
},
{
  // 售价
  src: "numberField_kewa74uu",
  cur: "numberField_kewa74uu"
},
{
  // 数量
  src: "numberField_kewa74uv",
  cur: "numberField_kewa74uv"
},
{
  // 单位
  src: "selectField_kewa74ux",
  cur: "selectField_kewa74ux"
},
{
  // 售价小计
  src: "numberField_kewa74uy",
  cur: "numberField_kewa74uy"
},
{
  // 备注
  src: "textareaField_kexnujd0",
  cur: "textareaField_kexnujd0"
},
{
  // 加密方式
  src: "selectField_kexnujd5",
  cur: "selectField_kexnujd5"
},
{
  // 产品模块
  src: "multiSelectField_kewa74uz",
  cur: "multiSelectField_kewa74uz"
},
{
  // 品牌
  src: "textField_kexnujd1",
  cur: "textField_kexnujd1"
},
{
  // 型号
  src: "textField_kexnujd2",
  cur: "textField_kexnujd2"
},
{
  // 供应商
  src: "multiSelectField_kfai2kue",
  cur: "multiSelectField_kfai2kue"
},
{
  // 采购价预计
  src: "numberField_kf0n0xsm",
  cur: "numberField_kf0n0xsm"
},
{
  // 状态
  src: "selectField_kf0n0xsj",
  cur: "selectField_kf0n0xsj"
},
{
  // 采购单价
  src: "",
  cur: "numberField_kf0tqfzg"
},
{
  // 采购折扣(%)
  src: "",
  cur: "numberField_kf0tqfzh"
},
{
  // 采购总价
  src: "",
  cur: "numberField_kf0tqfzi"
},
{
  // 返佣比例
  src: "",
  cur: "numberField_kf0tqfzl"
},
{
  // 应返佣金额
  src: "",
  cur: "numberField_kf0tqfzm"
},
{
  // 回写下单供应商
  src: "multiSelectField_kfai2kuf",
  cur: "multiSelectField_kfahy1nt"
}]

//---------------------- event ----------------------//

// 页面节点加载渲染完毕
export function didMount(ctx) {
  // ...
  setDetaiComplWidth(".node_kewa74ti", "230%")
}

let DETAILS_PRODUCTION = []
// 当合同编号发生变更
export function onContractChange(ctx) {
  ctx.store.get("selectField_kf0tqfz0").setVal("")  // 清空供应商
  _resetDetailData(ctx)
  const comp = ctx.store.get("selectField_kf0tqfyx")
  const condition = comp.getData().fieldData.value.split("-")[0]
  if (condition) _queryContractDetail(ctx, condition, comp)
}

// 当供应商变更
export function onSupplierChange(ctx) {
 // 过滤相同供应商
  const supplier = ctx.store.getData("selectField_kf0tqfz0").fieldData.value
  const detailsT = DETAILS_PRODUCTION.filter(detail => {
    // 多选过滤
    const suppliers = detail["multiSelectField_kfai2kue"].fieldData.value || []
    // 多选没有值,就没有这个字段,明细接口查询:可以被回写
    let supplied = detail["multiSelectField_kfai2kuf"]
    supplied = supplied ? supplied.fieldData.value || [] : []
    const isConfirm = detail["multiSelectField_kfai2kue"].fieldData.value.includes(supplier) && !supplied.includes(supplier)
    if (isConfirm) {
      // 记录选择供应商集合:不直接修改明细避免数据污染,新值记录
      supplied.push(supplier)
      detail["multiSelectField_kfahy1nt"] = { fieldData: { value: supplied } }
    }
    return isConfirm
  })
  ctx.store.get("tableField_kewa74uk").mergeVal(detailsT)
}
  • remark

关于侧边栏和免登:

无侧边栏, 在设置分享复制访问地址, 没有免登, PC端不会跳出钉钉浏览器: https://www.aliwork.com/alibaba/web/APP_EE7IAJLY0UQRRJOPHMN3/inst/formSubmit.html?formUuid=FORM-UR566DD1G3MH5QGQ2OF9F9I8KXGW288GT2PCKD#/

有侧边栏, 直接复制的有侧边栏的访问地址, 没有免登, PC端不会跳出钉钉浏览器: https://www.aliwork.com/alibaba/web/APP_EE7IAJLY0UQRRJOPHMN3/inst/homepage/#/FORM-UR566DD1G3MH5QGQ2OF9F9I8KXGW288GT2PCKD

无侧边栏, 在设置分享复制移动端访问地址, 有免登, PC端跳出钉钉在浏览器打开: https://www.aliwork.com/alibaba/web/APP_EE7IAJLY0UQRRJOPHMN3/inst/formSubmit.html?formUuid=FORM-UR566DD1G3MH5QGQ2OF9F9I8KXGW288GT2PCKD&corpid=dingebe19bcf228f85ec35c2f4657eb6378f&dd_addcookie=true


工作台4个任务和列表页面, 不能隐藏侧边栏 :: 免登追加corpid=xxx&dd_addcookie=true