首页 > 其他分享 >YonBuilder应用构建实战案例-体检管理(下)

YonBuilder应用构建实战案例-体检管理(下)

时间:2023-11-21 15:04:11浏览次数:30  
标签:实战 get viewModel kit item let YonBuilder 体检 getGridModel

紧接着上篇文章,我们将继续讲解体检管理案例操作。

体检单

一、业务对象

创建体检单业务对象,以下是业务对象下的实体为主子孙结构

1.体检单

YonBuilder应用构建实战案例-体检管理(下)_应用构建

2. 体检项目_体检单子表

YonBuilder应用构建实战案例-体检管理(下)_应用构建_02

二、页面建模

1、根据业务对象创建一主多子结构单据命名为体检单

2、设置主表字段不允许修改

组织、客户、累计出库年月、单据状态

3、设置子表所有字段不允许修改 体检项目、物料、二维码编号、是否检测、检测结果、检测人

4、删除子表的新增、删行按钮

5、设置子表表格的编辑方式为侧滑弹框

6、 体检单列表页面删除新增按钮

7 、配置体检按审批流

8、配置业务流

YonBuilder应用构建实战案例-体检管理(下)_应用构建_03

9、体检预约下推体检单 防止重复下推: isNull(getValue("AT1772AFF21C18000A.AT1772AFF21C18000A.examination_bill","id","source_id",id))

错误提示语:下游数据已存在

触发设置:手工触发

生单方式:默认新增单据

转换规则配置如下图

YonBuilder应用构建实战案例-体检管理(下)_应用构建_04

YonBuilder应用构建实战案例-体检管理(下)_应用构建_05

10、体检单下推销售订单目标交易类型:体检

过滤设置增加防止重复下推:isNull(getValue("voucher.order.Order","id","srcBill",id))&&verifystate==2

错误提示语:下游单据已存在或单据未审核

触发设置:手工触发

生单默认: 默认新增单据,勾选转单画面

转换规则配置如下图:

YonBuilder应用构建实战案例-体检管理(下)_应用构建_06

YonBuilder应用构建实战案例-体检管理(下)_应用构建_07

YonBuilder应用构建实战案例-体检管理(下)_应用构建_08

使用到的公式:

税目税率id : getValue("bd.taxrate.TaxRateVO","id","code","VAT0")

本币pk :getValue("bd.currencytenant.CurrencyTenantVO","id","code","CNY")

汇率类型id: getValue("bd.exchangeRate.ExchangeRateTypeVO","id","code","01")

单据日期: dateFormat(sysdate(),"yyyy-MM-dd")

11、体检单列表页面增加月底汇总出库按钮,点击汇总消耗品生成其他出库单(1).创建业务对象月底汇总

YonBuilder应用构建实战案例-体检管理(下)_应用构建_09

(2)、根据业务对象创建一个卡片页面,删除列表页面

(3)、将卡片页面设置为模态框,具体操作省略

(4)、保存按钮取消保存规则,然后增加保存函数

函数中调用了其他出库单接口,具体调用接口API脚本查看

viewModel.get('btnSave') && viewModel.get('btnSave').on('click', function (data) {
  // 确定--单击
  let date = viewModel.get("date").getValue();
  let org = viewModel.get("org").getValue();
  let warehouse = viewModel.get("warehouse").getValue();
  let use_version = viewModel.get("use_version").getValue();
  cb.rest.invokeFunction("AT1772AFF21C18000A.api.monthUsePro", {"date":date,"org":org,"warehouse":warehouse,"use_version":use_version},
  function(err, res) {
    if(err!=null){
      cb.utils.alert(err);
      return ;
    }else{
      cb.utils.alert("发起请求成功,请到其他出库节点查看");
      viewModel.communication({type:'modal',payload:{data:false}});
    }
  })
});

(5)、调用出库单API脚本

获取日期函数

let AbstractTrigger = require('AbstractTrigger');
  class MyTrigger extends AbstractTrigger {
    execute(context,param){
      Date.prototype.format = function(fmt)
      { 
      var o = {
      "M+" : this.getMonth()+1, //月份
      "d+" : this.getDate(), //日
      "h+" : this.getHours()%12 == 0 ? 12 : this.getHours()%12, //小时
      "H+" : this.getHours(), //小时
      "m+" : this.getMinutes(), //分
      "s+" : this.getSeconds(), //秒
      "q+" : Math.floor((this.getMonth()+3)/3), //季度
      "S" : this.getMilliseconds() //毫秒
      };
    if(/(y+)/.test(fmt))
    fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
    for(var k in o)
    if(new RegExp("("+ k +")").test(fmt))
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
    return fmt;
    } 
    let date = new Date().format(context);
    return {date};
    }
  }
  exports({"entryPoint":MyTrigger});

汇总统计耗材

let AbstractAPIHandler = require('AbstractAPIHandler');
  class MyAPIHandler extends AbstractAPIHandler {
   execute(request){
      let date = request.date; //获取日期
      let org = request.org;//组织
      let warehouse = request.warehouse; //仓库
      let use_version = request.use_version //版本
      // 因为两张表没有直接的关联关系,所以不能联查
      let sql = "select project as projectId,count(id) as num from AT1772AFF21C18000A.AT1772AFF21C18000A.examination_project "+
                " where yearmonth = '"+date+"' group by project";
      let projectNum = ObjectStore.queryByYonQL(sql);
      if(projectNum.length==0){
        throw new Error("为查找到数据");
      }
      //筛选出体检项
      var sqlConditionParamsStr = "";
      var projectMap = {}; //存储每个耗材使用了几份
      projectNum.forEach((project)=>{
        let kitId = "'"+project.projectId+"',";
        sqlConditionParamsStr+=kitId;
        projectMap[project.projectId]=project.num;
      })
      sqlConditionParamsStr = "("+substring(sqlConditionParamsStr, 0, sqlConditionParamsStr.length-1)+")";
      //根据查询条件查出耗材
      let consumptionSql = "select product,unit as stockUnitId,num,project_id from AT1772AFF21C18000A.AT1772AFF21C18000A.consumption "+
                " where project_id in "+sqlConditionParamsStr;
      let consumptionData = ObjectStore.queryByYonQL(consumptionSql);
      //循环重新赋值计算,将耗材数量统计
      consumptionData.forEach((data)=>{
        let totalConsumptionNum = projectMap[data.project_id]*data.num;
        data.qty = totalConsumptionNum;
        data.subQty = totalConsumptionNum;
        data._status = "Insert";
        data.othOutRecordsDefineCharacter = {"item02":data.project_id}
      })
      //拼装请求参数
      let othoutrecordParams = {};
      othoutrecordParams["org"] = org;
      othoutrecordParams["accountOrg"] = org;
      othoutrecordParams["bustype"] = "A10001";
      othoutrecordParams["_status"] = "Insert";
      othoutrecordParams["warehouse"] = warehouse;//仓库     
      othoutrecordParams["othOutRecords"] = consumptionData;
      othoutrecordParams["resubmitCheckKey"] = MD5Encode(JSON.stringify(othoutrecordParams)+use_version);
      let othoutrecordFun = extrequire("AT1772AFF21C18000A.backend.othoutrecord");
      othoutrecordFun.execute(othoutrecordParams);
   return {};
 }
}
exports({"entryPoint":MyAPIHandler});

其他出库单保存接口

let AbstractTrigger = require('AbstractTrigger');
  class MyTrigger extends AbstractTrigger {
    execute(context,param){
      //取得日期格式
      let date = extrequire("AT1772AFF21C18000A.backend.getDate");
      let dateFormat = date.execute("yyyy-MM-dd");
      context.vouchdate = dateFormat.date; //给单据日期赋值
      //调用接口
      let apiAction = "othoutrecord";
      let configFun = extrequire("AT1772AFF21C18000A.backend.config");
      let configRes = configFun.execute(null);
      let reqType = "POST";
      let body = {"data":context};
      let apiResponse;
      try{
        apiResponse = openLinker(reqType, configRes.apiurl[apiAction], configRes.appcode, JSON.stringify(body));
        let apiResponseData = JSON.parse(apiResponse);
        if(apiResponseData.code!=200){
          throw new Error("日志处理");
        }
      }catch(e){
        //处理日志,调用接口是避免回滚
        //取得日期格式
        let dateLog = extrequire("AT1772AFF21C18000A.backend.getDate");
        let dateFormatLog = dateLog.execute("yyyy-MM-dd HH:mm:ss");
        var error = {body:JSON.stringify(body),apiResponse:apiResponse,apiAction:apiAction,billnum:"examination_kit",createTime:dateFormatLog.date};
        openLinker(reqType, configRes.apiurl["apilog"], configRes.appcode, JSON.stringify(error));
        throw new Error("生成其他出库失败,请检查");
      }
     return {};
    }
  }
  exports({"entryPoint":MyTrigger});

体检单增加汇总耗材按钮,并增加点击事件打开创建的弹框

viewModel.get('button22rk') && viewModel.get('button22rk').on('click', function (params) {
  // 月终汇总出库--单击
  let data = {
    //TODO:填写单据类型及单据号
    billtype: 'Voucher',// 单据类型
    billno: 'monthtotal',// 单据号
    params: {
      mode: 'add',
    },
  };
  //打开一个单据,并在当前页面显示
  cb.loader.runCommandLine('bill', data, viewModel);
});

移动端

注:移动端和pc端公用元数据、工作流、业务流,后端函数可公用。移动端的页面和前端脚本以及发布应用是独立的。

一.体检预约单

(一).列表页

1.页面设计

调整列表页显示字段:姓名、手机号、身份证号、体检套餐、预约日期、价格。如下图

YonBuilder应用构建实战案例-体检管理(下)_应用构建_10

2. 功能(只看本人创建的预约单)

viewModel.on('beforeSearch',(args)=>{
  let userId = cb.context.getUserId();
  args.params.condition.simpleVOs=[
    {
      "field": "creator",
      "op": "eq",
      "value1": userId
    }
  ];
})

(二) .详情页

1.页面设计

表头: 手机号、姓名、客户分类:必填; 身份证号:必填 根据身份证号自动计算年龄和性别; 价格:不可修改,设置公式sum子表的使用金额,所有数值的精度为2 性别、年龄、客户:不可修改

效果如下图:

YonBuilder应用构建实战案例-体检管理(下)_应用构建_11

子表: 体检项目必填,其余字段不可编辑; 选择体检项目,其余字段自动带入;

效果如下图:

YonBuilder应用构建实战案例-体检管理(下)_应用构建_12

  1. 功能:根据身份证号自动计算年龄和性别
//身份证带出年龄和性别
viewModel.get('identity').on('afterValueChange',(data)=>{
  debugger;
  if(data && data.value != "") {
    let identity = JSON.parse(data.value).identity;
    let sex = parseInt(identity.substr(16, 1)) % 2 == 1 ? '1' : '2';
    let age = getAge(identity);
    viewModel.get('gender').setValue(sex);
    viewModel.get('age').setValue(age);
  }

})   

function getAge(userCard) {
  var myDate = new Date();
  var month = myDate.getMonth() + 1;
  var day = myDate.getDate();
  var age = myDate.getFullYear() - userCard.substring(6, 10) - 1;
  if (userCard.substring(10, 12) < month || userCard.substring(10, 12) == month && userCard.substring(12, 14) <= day) {
      age++;
  }
  return age;
}

3 功能:详情增加动作扩展(和pc动作通用)

YonBuilder应用构建实战案例-体检管理(下)_应用构建_13

4 子表功能:体检项目必填,其余字段不可编辑

//editRowModel的字段状态
viewModel.on('afterLoadData',(args)=>{
  viewModel.getGridModel().getEditRowModel().get("project_name").setState('bIsNull',false);
  viewModel.getGridModel().getEditRowModel().get("examination_kit_name").setState('bCanModify',false);
  viewModel.getGridModel().getEditRowModel().get("original_price").setState('bCanModify',false);
  viewModel.getGridModel().getEditRowModel().get("price").setState('bCanModify',false);
  viewModel.getGridModel().getEditRowModel().get("is_kit").setState('bCanModify',false);
  viewModel.getGridModel().getEditRowModel().get("useprice").setState('bCanModify',false);
  viewModel.getGridModel().getEditRowModel().get("product_standard_name").setState('bCanModify',false);
})

5、子加粗样式表功能:选择体检项目,其余字段自动带入

//选择项目带入其他字段
viewModel.getGridModel().getEditRowModel().get("project_name").on("afterValueChange",(args)=>{ 
  let index = viewModel.getGridModel().getFocusedRowIndex();
  viewModel.getGridModel().getEditRowModel().get('original_price').setValue(args.value.original_price);
  viewModel.getGridModel().setCellValue(index,"original_price",args.value.original_price);
  viewModel.getGridModel().setCellValue(index,'original_price',args.value.original_price);
  // 所属套餐 examination_kit_name
  viewModel.getGridModel().getEditRowModel().get('examination_kit').setValue(args.value.examination_kit_id);
  viewModel.getGridModel().setCellValue(index,'examination_kit',args.value.examination_kit_id);
  viewModel.getGridModel().getEditRowModel().get('examination_kit_name').setValue(args.value.examination_kit_id_name);
  viewModel.getGridModel().setCellValue(index,'examination_kit_name',args.value.examination_kit_id_name);
  //套餐价格
  viewModel.getGridModel().getEditRowModel().get('price').setValue(args.value.price);
  viewModel.getGridModel().setCellValue(index,'price',args.value.price);
  //是否套餐
  viewModel.getGridModel().getEditRowModel().get('is_kit').setValue("2");
  viewModel.getGridModel().setCellValue(index,'is_kit',"2");
  //使用金额
  viewModel.getGridModel().getEditRowModel().get('useprice').setValue(args.value.original_price,true);
  viewModel.getGridModel().setCellValue(index,'useprice',args.value.original_price);
  //物料
  viewModel.getGridModel().getEditRowModel().get('product_standard').setValue(args.value.product);
  viewModel.getGridModel().setCellValue(index,'product_standard',args.value.product);
  viewModel.getGridModel().getEditRowModel().get('product_standard_name').setValue(args.value.product_name);
  viewModel.getGridModel().setCellValue(index,'product_standard_name',args.value.product_name);
})

6、子表功能:编辑页,拦截保存动作

viewModel.get("btnSubSave_1").on('click',(data)=>{
  if(!viewModel.getGridModel().getEditRowModel().get("project_name").getValue()) {
    cb.utils.alert("项目不能为空");
    return false;
  }
  viewModel.biz.do('closePage',viewModel);
})

7、主子联动:选择主表所属套餐,子表自动增加下属项目

let gridModel = viewModel.getGridModel();
viewModel.get("pay_examination_kitList").on("afterValueChange",function(data){
  //旧的数据  套餐id放数组
  let oldData = gridModel.getDataSourceRows();
  let oldRows = gridModel.getRows();
  let rowsDataState = gridModel.__data.rowsDataState
  let delIndex = [];
  if(data && data.value.length > 0) {
    let result = cb.rest.invokeFunction("AT17925AEA1C18000A.api.getProjectById", {data:data.value},function(err, res) {},viewModel, {async:false});
    debugger;
    //新的数据  套餐id放数组
    let newData = result.result.res;
    let newKit = [];
    newData.forEach((item,index)=>{
      newKit.push(item.examination_kit);
    })

    let oldKit = [];
    oldRows.forEach((item,index)=>{
      if(item.is_kit == '1') {
        oldKit.push(item.examination_kit);
      }
    })
    //多的数据就新增
    let addRows = [];
    newData.forEach((item,index)=>{
      if(!oldKit.includes(item.examination_kit)) {
        item.is_kit = '1';
        addRows.push(item);
        rowsDataState.push("Insert");
      }
    })
    //少的套餐就删除
    oldRows.forEach((item,index)=>{
      if(item.is_kit == '1' && !newKit.includes(item.examination_kit)) {
        delIndex.push(index);
      }
    })

    gridModel.setDataSource(oldData.concat(addRows));
    gridModel.__data.rowsDataState = rowsDataState;
    rowsDataState.forEach((item,index)=>{
      if(item == "Delete") {
        delIndex.push(index);
      }
    })
  } else {
    if(data) {
      oldRows.forEach((item,index)=>{
        if(item.is_kit == '1') {
          delIndex.push(index);
        }
      })
    }
  }
  viewModel.setCache('remark',1);
  gridModel.deleteRows(delIndex);
})

8、主子联动:子表删行,如果是删除的套餐项目,那么套餐中的其他项目更新使用金额=原价,是否套餐更新为否

gridModel.on('beforeDeleteRows',(args)=>{
  let mark = viewModel.getCache('remark');
  if(!mark) {
    if(args[0].is_kit == '1') {
      cb.utils.confirm("删除后不满足套餐的情况体检项将按照原价计算",function(){

      let rows = gridModel.getRows();
      let delIndex;
      rows.forEach((item,index)=>{
        //删除数据
        if(item._id == args[0]._id) {
          delIndex = index;
        } else {
          //更新同套餐数据
          if((item.examination_kit == args[0].examination_kit) && (item.is_kit == '1')) {
            item.is_kit = '2';
            item.useprice = item.original_price;
            gridModel.updateRow(index,item);
          }
        }
      })
      //删除数据
      viewModel.setCache('remark',1);
      gridModel.deleteRows([delIndex]);
      //更新表头套餐
      let kitList = viewModel.get("pay_examination_kitList").getData();
      let newKitList = [];
      let count = 0;
      kitList.forEach((item,index)=>{
        if(item.examination_kit == args[0].examination_kit) {
          if(item.id) {
            item._status = "Delete";
            newKitList.push(item);
          }
        } else {
          newKitList.push(item);
        }
      })
      debugger;
      viewModel.get("pay_examination_kitList").doPropertyChange('clearTag');
      viewModel.get("pay_examination_kitList").setData(newKitList);

      },function(){
        return false;
      })

      return false;
    }

  } else {
    viewModel.clearCache("remark");
  }
})

二、体检单

(一). 列表页

1、页面设计:字段调整如下图

YonBuilder应用构建实战案例-体检管理(下)_应用构建_14

2、功能:只看本人创建的客户的体检单

//前端脚本
viewModel.on('beforeSearch',(args)=>{
  let result = cb.rest.invokeFunction("AT17925AEA1C18000A.api.getCustomer", {},
  function(err, res) {},viewModel,{async:false})
  let arr = [];
  result.result.res.forEach((item,index)=>{
    arr.push(item.customer)
  })
  args.params.condition.simpleVOs=[
    {
      "field": "merchant",
      "op": "in",
      "value1": arr
    }
  ];
})
//后端脚本
let AbstractAPIHandler = require('AbstractAPIHandler');
  class MyAPIHandler extends AbstractAPIHandler {
   execute(request){
    let userId = ObjectStore.user().id;
    let sql = "select customer from AT17925AEA1C18000A.AT17925AEA1C18000A.pay where  creator= '"+ userId+"'";
    var res = ObjectStore.queryByYonQL(sql);
   return {res};

 }
}
exports({"entryPoint":MyAPIHandler});

(二).详情页

YonBuilder应用构建实战案例-体检管理(下)_应用构建_15

标签:实战,get,viewModel,kit,item,let,YonBuilder,体检,getGridModel
From: https://blog.51cto.com/u_16344526/8502281

相关文章

  • 2023版 STM32实战3 按键外部中断(电路与代码都讲解)
    常规电路(带上拉电阻)阻值可选3.3/4.7/5.1/10单位K  偷懒电路利用GPIO内部的上拉模式 代码(直接拷贝使用)这是一个外部中断控制变量a增加减少的demo为了新手方便我直接都写在了main.c文件 #include"stm32f10x.h"u8keyflag=0;u8a=0;voidKEY_Init(void){......
  • 决策树C4.5算法的技术深度剖析、实战解读
    在本篇深入探讨的文章中,我们全面分析了C4.5决策树算法,包括其核心原理、实现流程、实战案例,以及与其他流行决策树算法(如ID3、CART和RandomForests)的比较。文章不仅涵盖了丰富的理论细节和实际应用,还提出了独特的洞见,旨在帮助读者全面了解C4.5算法的优缺点和应用场景。关注Tech......
  • FlutterApp实战·第01天:Flutter安装和配置
    Flutter安装文档:官方文档:https://docs.flutter.dev/get-started/install中文文档:https://flutter.cn/docs/get-started/installDart升级Dart安装参考前面文章(Dart安装和初体验):https://ntopic.cn/p/2023092301后续学习我们采用最新Flutter版本,因此建议升级Dart最新版本(当前......
  • 探索人工智能的世界:构建智能问答系统之实战篇
    引言前面我们已经做好了必要的准备工作,包括对相关知识点的了解以及环境的安装。今天我们将重点关注代码方面的内容。如果你已经具备了Java编程基础,那么理解Python语法应该不会成为问题,毕竟只是语法的差异而已。随着时间的推移,你自然会逐渐熟悉和掌握这门语言。现在让我们开始吧!环......
  • 2023版 STM32实战1 LED灯驱动(电路与代码都讲解)
    电路图常规画法(带限流电阻计算公式)设LED电流为20mA(统一单位为0.02A)电压为3.3V限流电阻=(电源电压-负载正向工作电压)/工作电流限流电阻=(5V-3.3V)/0.02mA=1.7/V0.02A=85R省事画法(直接用IO输出)代码(直接拷贝使用)找对引脚!!!这是一个LED闪烁的demo为了新手方便我直接都写在了main.c......
  • Angular 应用实现 Lazy Load(懒加载)的项目实战经验分享
    笔者之前两篇掘金社区文章,分别介绍了企业级Angular应用开启PWA特性和服务器端渲染,从而提升用户体验的两种设计思路:Angular应用支持PWA(ProgressiveWebApplication)特性的开发步骤分享基于AngularUniversal引擎进行服务器端渲染的前端应用StateTransfer故障排查......
  • Angular 应用的搜索引擎优化(SEO)实战指南
    笔者之前在掘金社区发表了两篇关于Angular开发的文章,分别介绍了Angular支持服务器端渲染和PWA的技术细节:基于AngularUniversal引擎进行服务器端渲染的前端应用StateTransfer故障排查案例Angular应用支持PWA(ProgressiveWebApplication)特性的开发步骤分享本......
  • 2023版 STM32实战1 LED灯驱动(电路与代码都讲解)
    电路图常规画法(带限流电阻计算公式) 设LED电流为20mA(统一单位为0.02A)电压为3.3V限流电阻=(电源电压-负载正向工作电压)/工作电流限流电阻=(5V-3.3V)/0.02mA=1.7/V0.02A=85R省事画法(直接用IO输出)  代码(直接拷贝使用)找对引脚!!!这是一个LED闪烁的demo为了新手方便我......
  • Dapper NetCore 分区实战
    在上一篇中介绍了基于Dapper的NetCore分表,本篇旨在介绍基于Dapper的NetCore分区,废话不多说开搞吧!模拟业务场景:基于公司所在地区对表建立分区设计公司表结构,其中TableAttribute标识表名,PartitionAttribute标识当前表是分区结构,Property代表按照某个属性分区publicabstractc......
  • HAProxy 入门实战(2)--简单使用
    本文主要介绍HAProxy的实际使用,文中所使用到的软件版本:Centos7.9.2009、HAProxy2.8.2。1、全局配置全局配置位于global部分,该部分的参数是进程范围的,通常特定于操作系统。它们通常仅设置一次,并且在设置正确后不需要更改。其中一些参数具有命令行等效项。globallog127.0.0......