列表卡片单页应用缓存方案

为了解决多页应用中页面之间数据交互,平台提供了多页工程中嵌套单页应用的方案:同一功能节点间的页面若有频繁的数据交互,则可把该节点做成单页应用,同一单页应用中的数据统一在ViewModal中管理。

1. 列表卡片易用性改进需求

1.1 需要自动加载数据的应用,进入应用时,按默认快速查询方案加载数据。
1.2 卡片界面返回列表界面时:

  1. 从哪来回哪去:原来是哪个页签、第几页,则返回哪个页签、第几页,显示原来数据;(—-平台处理)
  2. 将当前列表页中清除已删除行.
    1. 单据列表20条数据,分两页,在第一页点删除3行后的界面效果:在列表界面第一页显示7行;若当前页数据都删除,则显示空列表界面;(—-平台处理)
    2. 卡片显示时如果删除了单据,返回列表时原有分页不变,去除当前页其中被删除的数据,如果列表中当前页所有行都被删除,则显示该空白页 (—-平台处理)
    3. 当前列表分页签情况下,做提交、审批等处理,只更新单据的状态,不在当前页签清除,也不更新页签上的数量;切换页签或点查询时,再做更新(—-页签状态领域处理)
    4. 批量处理时同样的效果。(—-页签状态领域自己处理)
    5. 卡片界面浏览态删除后,跨页翻行机制下,显示下一条单据且浏览态,如果是最后一页最后一行,则显示上一行,若无数据则显示空界面;空界面按钮只有新增类型的按钮。(—-平台提供判断条件,领域自己处理)
  3. 将新增数据添加在当前页最后一条后。
    1. 例如:单据列表查询后三条记录,一页默认显示3条,点新增,编辑后保存,点返回,回到列表界面,显示为4条记录;(—-平台处理)
    2. 查询后在列表界面,点击进入卡片显示后,可以下翻页到下个单据,下翻单据时是支持跨页下翻上翻的。即使翻页翻到其他分页的数据上,返回列表也是从哪来回哪去。(—-平台处理)
    3. 当前列表分页签情况下,新增时如何处理:新增数据添加到当前页签当前分页下,不自动更新页签上的数量,也不更新下面分页上的数据条数。(—-页签状态,领域处理)
    4. 例如:15条数据,一页10条,在第一页,点新增,新增了3条数据,在卡片下翻下一行,翻到最后一行,新增一条数据,返回列表第一页,则第一页显示14条;(—-平台处理)
  4. 分页切换时按当前数据重新分页。数据有删除时因重新分页导致的下一页可以为空白页;
    1. 例1:13条数据,一页显示10行,在第一页删除三条数据,切下一页时,重新分页,显示第二页为空白页,所有数据都到了第一页。(—-平台处理)
    2. 例2:13条数据,一页显示10行,在第一页点浏览进入卡片,切到第二页最后一行,这时点新增,保存,留在新增行的浏览态,这时候卡片中翻上一条,显示列表第一页的第10行数据,下一条是列表第二页的第一行数据。(—-平台处理)
  5. 页签切换时,做刷新处理,并更新各页签上数量.(—-领域处理)

    2. 列表卡片适配方案

    2.1 构建列表卡片单页应用

    将列表卡片节点构建为一个单页应用,添加路由文件,将列表和卡片分别注册为同一个单页应用的两个节点。
    单页应用的构建例子:请点击此处。
    (1) 将列表、卡片组件输出 ``` SaleOrderList = createPage({})(SaleOrderList);

//删除 //ReactDOM.render(, document.querySelector(‘#app’));

//改为: export default SaleOrderList;

  1. (2)增加路由文件<br />![muli.png](https://cdn.nlark.com/yuque/0/2022/png/28735069/1652686595377-517cd634-e1f3-4a85-b914-26a5c27d82b9.png#clientId=ueacef7cd-063c-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=uee2a404c&margin=%5Bobject%20Object%5D&name=muli.png&originHeight=256&originWidth=355&originalType=binary&ratio=1&rotation=0&showTitle=false&size=5795&status=done&style=none&taskId=ub7215996-0603-4c5e-84e1-25d1e0368b5&title=)<br />(3)路由文件如下<br />_注意:路由文件asyncComponent引入的文件必须加上webpackChunkName和 /* webpackMode: "eager" / ,引入第三方库时在import里也需要加入注释 / webpackMode: "eager" */_<br />![router11.png](https://cdn.nlark.com/yuque/0/2022/png/28735069/1652686610039-86173fde-840a-4212-bd2c-114e46556cf6.png#clientId=ueacef7cd-063c-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=u85d1bec3&margin=%5Bobject%20Object%5D&name=router11.png&originHeight=524&originWidth=1630&originalType=binary&ratio=1&rotation=0&showTitle=false&size=72253&status=done&style=none&taskId=u32008a25-6f2a-4d27-80d4-81b604a2d59&title=)<br />![index.png](https://cdn.nlark.com/yuque/0/2022/png/28735069/1652686617755-309ead9c-2b6c-4754-9e91-1fdbaa2b1412.png#clientId=ueacef7cd-063c-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=u94eebbc7&margin=%5Bobject%20Object%5D&name=index.png&originHeight=182&originWidth=565&originalType=binary&ratio=1&rotation=0&showTitle=false&size=7790&status=done&style=none&taskId=u81b0cc4e-bee2-4c81-93c9-54133367c10&title=)<br />(4) 注意:将列表卡片改为单页应用以后,则需要分别修改列表和卡片在小应用中的对应的注册路径。<br />(a). 若列表注册路由为:"/list",则小应用注册地址为:"领域名/模块名/节点名/main/index.html#/list".<br />(b). 若卡片注册路由为:"/card",则小应用注册地址为:"领域名/模块名/节点名/main/index.html#/card".
  2. <a name="LHa03"></a>
  3. ### 2.2 列表卡片改进方案前端适配步骤
  4. <a name="tb0Bu"></a>
  5. #### (1)单页应用内的页面跳转使用 pushTo
  6. 若:列表的路由注册路径为"/list",卡片的路由注册路径为"/card"。<br />则:列表跳卡片代码:props.pushTo("/card",{status:"browse",id:"10990250QW"});卡片跳列表代码:props.pushTo("/list")。<br />参数的获取(getUrlParam)、设置(setUrlParam)方法不变。
  7. <a name="J8QT3"></a>
  8. #### (2) 定义单页应用缓存数据命名空间dataSource.
  9. dataSource为标识缓存数据的字符串,建议命名规范为:"领域名.模块名.节点名.自定义名"。<br />_**注意:同一单页应用中,列表和卡片的dataSource相同**。_
  10. <a name="ng59f"></a>
  11. #### (3) 列表若分页签,页签数量的更新需要领域自行处理
  12. _对于类似页签数量等非表格区的数据需要缓存的,使用(4 方法各领域自己处理。_
  13. <a name="TOL81"></a>
  14. #### (4) 平台提供领域自己处理缓存数据API

import {cardCache} from “nc-lightapp-front”; let {setDefData, getDefData } = cardCache;

/*

  • key:存储数据的key
  • dataSource: 缓存数据命名空间
  • data: 存储数据 */ setDefData(key, dataSource, data);

/*

  • key:存储数据的key
  • dataSource: 缓存数据命名空间 */ getDefData(key, dataSource)
    <a name="isC61"></a>
    #### (5) 列表适配步骤
    _**(5.1) 给列表表格(createSimpleTable)增加dataSource标识**_
    
    //表格加dataSource标识 //给表格加pkname: 表格数据的主键名字(key) createSimpleTable(“tableAreacode”,{ dataSource: dataSource, pkname: pkname, componentInitFinished:()=>{
      //缓存数据赋值成功的钩子函数
      //若初始化数据后需要对数据做修改,可以在这里处理
    
    } })
    **_(5.2) 卡片页面返回列表页面(初始化)时,先判断是否已经存在缓存数据,若存在缓存数据,则不需要重新查询给表格赋值,否则发起查询请求,加载表格数据._**_ 注意:查询和刷新按钮需要重新查询,请求后台数据。_
    
    let { hasCacheData } = this.props.table;

/*

  • dataSource: 缓存数据命名空间 */ if(!hasCacheData(dataSource){ ajax({
      url:"...",
      data:"...",
      success:(res)=>{
          if(res && res.data && res.data["tableAreacode"]){
               this.props.table.setAllTableData(res.data["tableAreacode"]);
          }
      }
    
    }) }
    **_(5.3) 删除列表表格数据时,删除成功后,需要调用平台方法deleteCacheId,其他处理逻辑不变_**
    
    let {deleteCacheId} = this.props.table;

//删除成功后, 调用该方法删除缓存中对应id

/*

  • tableAreacode:表格区域编码
  • pkvalue:数据主键的值 */ deleteCacheId(tableAreacode,pkvalue);
    **_(5.4) 新增列表表格数据时,新增成功后,需要调用平台方法addCacheId,其他处理逻辑不变_**
    
    let {addCacheId} = this.props.table; //新增成功后, 调用该方法新增缓存中对应id

/*

  • tableAreacode:表格区域编码
  • pkvalue:数据主键的值 */ addCacheId(tableAreacode,pkvalue);
    <a name="mSWBj"></a>
    #### (6)卡片适配步骤
    _**(6.1) 给翻页组件cardPagination添加dataSource标识**_
    
    //翻页组件加dataSource标识 createCardPagination({ handlePageInfoChange: ()=>{}, dataSource: dataSource })
    **_(6.2) 卡片翻页查询数据时,先判断是否有缓存数据,若是,取缓存数据给卡片赋值;否则,发起查询请求,并把请求回来的数据更新到缓存中_**
    
    import {cardCache} from “nc-lightapp-front”; let { getCacheById, updateCache } = cardCache;

/*

  • id:数据主键的值
  • dataSource: 缓存数据命名空间 */ let cardData = getCacheById(id, dataSource);

if(cardData){ this.props.form.setAllFormValue({“formAreacode”:cardData.head[“formAreacode”]}); this.props.cardTable.setTableData(“tableAreacode”,cardData.body[“tableAreacode”]); }else{ //发起ajax请求查询数据 ajax({ url:, data:, success:(res)=>{ //给界面组件赋值

        /*
        * idname: 数据主键的命名
        * id:数据主键的值
        * headAreacode: 卡片表头的区域编码
        * dataSource: 缓存数据命名空间
        */
        updateCache(idname,id,res.data,headAreacode,dataSource);
    }
})

}

**_(6.3) 在列表点新增或在卡片上点新增按钮后,新增保存时,保存成功后,需要调用addCache方法,将数据存储到缓存中_**

import {cardCache} from “nc-lightapp-front”; let { addCache } = cardCache;

ajax({ url:, data, success:(res)=>{ //将返回数据赋值到界面上

    //将返回数据存储到缓存中
    /*
    * id:数据主键的值
    * headAreacode: 卡片表头的区域编码
    * dataSource: 缓存数据命名空间
    */
    addCache(id,res.data,headAreacode,dataSource);

    //另外还需要设置下卡片翻页组件的当前id
    this.props.cardPagination.setCardPaginationId({id: id})
}

})

**_(6.4) 卡片上点击新增按钮后,未保存,又点了取消按钮, 则需调用方法getCurrentLastId,获取当前列表最后一条数据渲染界面_**

import {cardCache} from “nc-lightapp-front”; let {getCurrentLastId } = cardCache;

let id = getCurrentLastId(dataSource);

//根据id获取下条数据:取下条数据时也要同第2步一样,先从缓存中取,缓存里没有再发数据请求。

**_(6.5) 卡片修改保存时,保存成功后,需要调用updateCache方法,将数据更新到缓存中_**

import {cardCache} from “nc-lightapp-front”; let { updateCache } = cardCache;

ajax({ url:, data, success:(res)=>{ //将返回数据赋值到界面上

    //将返回数据存储到缓存中
    /*
    * idname: 数据主键的命名
    * id:数据主键的值
    * headAreacode: 卡片表头的区域编码
    * dataSource: 缓存数据命名空间
    */
    updateCache(idname,id,res.data,headAreacode,dataSource);
}

})

**_(6.6) 在卡片删除数据时,删除成功后,先通过getNextId获取下条数据的主键,再调用deleteCacheById方法,将缓存数据删除_**

import {cardCache} from “nc-lightapp-front”; let { getNextId, deleteCacheById } = cardCache;

ajax({ url:, data, success:(res)=>{ //删除成功!

    let id = this.props.getUrlParam("id");

    //根据当前id,获取下个id
    /*
    * id:数据主键的值
    * dataSource: 缓存数据命名空间
    */
    let nextId = getNextId(id, dataSource);

    //调用删除缓存数据方法
       /*
    * idname: 数据主键的命名
    * id:数据主键的值
    * dataSource: 缓存数据命名空间
    */
    deleteCacheById(idname,id,dataSource);

    //根据nextId查询下条数据
    //注意:查询下条数据时,也同样需要先判断有没有缓存数据,没有缓存数据时,再发查询请求。
    //----
}

})

**_(6.7) 点击翻页组件时,拿到新的id也要先判断是否存在对应的缓存数据,若存在,则取缓存数据给界面赋值;否则,发ajax请求查询数据,并将查询回的数据更新到缓存中_**

import {cardCache} from “nc-lightapp-front”;

export default function (props, pk) { let { getCacheById, updateCache } = cardCache; props.setUrlParam(pk); /*

* id:数据主键的值
* dataSource: 缓存数据命名空间
*/
let cardData = getCacheById(id, dataSource);

if(cardData){
    this.props.form.setAllFormValue({"formAreacode":cardData.head["formAreacode"]});
    this.props.cardTable.setTableData("tableAreacode",cardData.body["tableAreacode"]);
}else{
    //发起ajax请求查询数据
    //给界面组件赋值

    /*
    * idname: 数据主键的命名
    * id:数据主键的值
    * headAreacode: 卡片表头的区域编码
    * dataSource: 缓存数据命名空间
    */
    updateCache(idname,id,headAreacode,dataSource);
}

} ```