我们都知道,报表有个功能为导出excel,但是有的时候客户需求往往标准的报表达不到,比如导出excel,其中本月修改的数据字段标红,如下图所示。

这就需要我们去写VF来实现此功能。

需求:将数据表记录导出成excel,其中excel内容需要本月修改的数据字段自动标红显示。

表:Goods__c,字段如下:

设计思路:如果导出excel并且需要跟踪每个字段的修改时间进行校验是否标红,则需要有一个表取跟踪这个表.有两种方式进行Track。

1.salesforce中提供了Track History功能,即当表字段小于20个情况下,可以通过设置Track History,那样系统会自动创建相关表的History表,在这个demo中系统会自动创建Goods_History表。详细Track History知识可以参看以下链接:

https://help.salesforce.com/apex/HTFederatedSearchResults#q=Track%20History&t=TopResultTab&sort=relevancy&f:@sflanguage=[en_US]

2.当表的字段超过20个,则通过Track History便无法满足需求了,这种情况我们需要自己创建一个sObject来和需要track的sObject进行关联,如下图所示:

页面显示均使用标准生成的页面,在Goods__c修改情况下,需要设置Trigger自动插入此条Goods__c记录对应的GoodsSign__c记录,如果某个字段有修改,则设置某个字段相对应的Date信息为System.today().

制作步骤:

1.写GoodsTrigger.Trigger代码如下所示:

 trigger GoodsTrigger on Goods__c (before delete, before update) {
if(trigger.isBefore) {
//get the Schema Information of GoodsSign to check whether current user has access to operate the data
Schema.DescribeSObjectResult goodsSignDescribe = GoodsSign__c.sObjectType.getDescribe();
List<GoodsSign__c> goodsSignList = new List<GoodsSign__c>();
//第一个参数为Goods__c的ID,第二个参数为GoodsSign__c
Map<ID,GoodsSign__c> goodsIdToGoodsSignMap = new Map<ID,GoodsSign__c>();
if(trigger.isUpdate) {
Set<ID> goodsIdSet = new Set<ID>();
if(goodsSignDescribe.isCreateable() && goodsSignDescribe.isUpdateable()) {
List<Goods__c> goodsNewList = trigger.new;
List<Goods__c> goodsOldList = trigger.old;
for(Goods__c goods : goodsNewList) {
goodsIdSet.add(goods.Id);
}
String fetchGoodsSignByGoodsId = 'SELECT CreatedById, CreatedDate,' +
' IsDeleted, GoodsBrandDate__c, GoodsCostPriceDate__c,' +
' GoodsDescribeDate__c, GoodsId__c, GoodsNameDate__c,' +
' GoodsPriceDate__c, Name, LastModifiedById, LastModifiedDate,' +
' OwnerId, Id, SystemModstamp FROM GoodsSign__c ' +
' where GoodsId__c in :goodsIdSet';
List<GoodsSign__c> tempGoodsSignList = Database.query(fetchGoodsSignByGoodsId);
for(GoodsSign__c goodsSign : tempGoodsSignList) {
goodsIdToGoodsSignMap.put(goodsSign.GoodsId__c,goodsSign);
}
for(Integer i=0;i<goodsNewList.size();i++) {
Goods__c goodsNew = goodsNewList.get(i);
Goods__c goodsOld = goodsOldList.get(i);
GoodsSign__c goodsSign = new GoodsSign__c();
Id goodsId = goodsNew.Id;
if(goodsIdToGoodsSignMap.get(goodsId) != null) {
goodsSign = goodsIdToGoodsSignMap.get(goodsId);
} if(goodsNew.GoodsName__c != goodsOld.GoodsName__c) {
goodsSign.GoodsNameDate__c = System.today();
}
if(goodsNew.GoodsPrice__c != goodsOld.GoodsPrice__c) {
goodsSign.GoodsPriceDate__c = System.today();
}
if(goodsNew.GoodsCostPrice__c != goodsOld.GoodsCostPrice__c) {
goodsSign.GoodsCostPriceDate__c = System.today();
}
if(goodsNew.GoodsBrand__c != goodsOld.GoodsBrand__c) {
goodsSign.GoodsBrandDate__c = System.today();
}
if(goodsNew.GoodsDescribe__c != goodsOld.GoodsDescribe__c) {
goodsSign.GoodsDescribeDate__c = System.today();
}
if(goodsSign.GoodsId__c == null) {
goodsSign.GoodsId__c = goodsId;
}
goodsSignList.add(goodsSign);
}
if(goodsSignList.size() > 0) {
upsert goodsSignList;
}
} } else if(trigger.isDelete) {
//cascade delete
if(goodsSignDescribe.isDeletable()) {
List<Goods__c> goodsList = trigger.old;
Set<ID> goodsIdSet = new Set<ID>();
for(Goods__c currentGoods : goodsList) {
if(!goodsIdSet.contains(currentGoods.Id)) {
goodsIdSet.add(currentGoods.Id);
}
}
String fetchGoodsSignByGoodsIdSet = 'SELECT CreatedById, CreatedDate,' +
' IsDeleted, GoodsBrandDate__c, GoodsCostPriceDate__c, GoodsDescribeDate__c,' +
' GoodsId__c, GoodsNameDate__c, GoodsPriceDate__c, Name, LastModifiedById,' +
' LastModifiedDate, OwnerId, Id, SystemModstamp FROM GoodsSign__c' +
' where GoodsId__c in :goodsIdSet';
List<GoodsSign__c> goodsSignNeedDeleteList = Database.query(fetchGoodsSignByGoodsIdSet);
delete goodsSignNeedDeleteList;
}
}
}
}

此Trigger有两个功能:

1.当进行修改操作并且Goods__c记录有字段改变时,如果有相对应的GoodsSign__c进行对应,则update此记录,否则新建记录,并记录哪些字段有修改;

2.当进行删除操作时,如果有相对应的GoodsSign__c进行对应,则级联删除。

两者操作均需要当前用户有GoodsSign的操作权限。

2.新建一个类用来记录导出的字段以及导出的字段的颜色。

 public with sharing class GoodsExportObject {
public String goodsName{get;set;}
public String goodsNameColor{get;set;}
public String goodsBrand{get;set;}
public String goodsBrandColor{get;set;}
public String goodsPrice{get;set;}
public String goodsPriceColor{get;set;}
public String goodsCostPrice{get;set;}
public String goodsCostPriceColor{get;set;}
public String goodsDescribe{get;set;}
public String goodsDescribeColor{get;set;}
}

3.新建Controller,此Controller用来获取显示到excel的数据。

 public with sharing class ExportGoodsController {

     List<Goods__c> goodsList{get;set;}
List<GoodsSign__c> goodsSignList{get;set;}
public List<GoodsExportObject> exportGoodsList{get;set;}
Map<Id,GoodsSign__c> goodsSignMap = new Map<Id,GoodsSign__c>();
public ExportGoodsController() {
goodsList = new List<Goods__c>();
goodsSignList = new List<GoodsSign__c>();
exportGoodsList = new List<GoodsExportObject>();
String fetchAllGoods = 'SELECT CreatedById, CreatedDate, IsDeleted,' +
' Name, GoodsBrand__c, GoodsCostPrice__c, GoodsDescribe__c, GoodsName__c,' +
' GoodsPrice__c, LastActivityDate, LastModifiedById, LastModifiedDate,' +
' OwnerId, Id, SystemModstamp FROM Goods__c';
goodsList = Database.query(fetchAllGoods);
String fetchAllGoodsSign = 'SELECT CreatedById, CreatedDate, IsDeleted,' +
' GoodsBrandDate__c, GoodsCostPriceDate__c, GoodsDescribeDate__c,' +
' GoodsId__c, GoodsNameDate__c, GoodsPriceDate__c, Name, LastModifiedById,' +
' LastModifiedDate, OwnerId, Id, SystemModstamp FROM GoodsSign__c';
goodsSignList = Database.query(fetchAllGoodsSign);
for(GoodsSign__c goodsSign : goodsSignList) {
if(!goodsSignMap.containsKey(goodsSign.GoodsId__c)) {
goodsSignMap.put(goodsSign.GoodsId__c,goodsSign);
}
}
} public PageReference exportGoods() {
String bgColor = 'red';
Integer nowMonth = System.today().month();
for(Goods__c currentGoods : goodsList) {
GoodsSign__c goodsSign = goodsSignMap.get(currentGoods.Id);
GoodsExportObject tempGoodsExportObject = new GoodsExportObject();
tempGoodsExportObject.goodsName = currentGoods.GoodsName__c;
tempGoodsExportObject.goodsBrand = currentGoods.GoodsBrand__c;
tempGoodsExportObject.goodsPrice = String.valueOf(currentGoods.GoodsPrice__c);
tempGoodsExportObject.goodsCostPrice = String.valueOf(currentGoods.GoodsCostPrice__c);
tempGoodsExportObject.goodsDescribe = currentGoods.GoodsDescribe__c;
if(goodsSign != null) {
if(goodsSign.GoodsNameDate__c != null && goodsSign.GoodsNameDate__c.month() == nowMonth) {
tempGoodsExportObject.goodsNameColor = bgColor;
}
if(goodsSign.GoodsBrandDate__c != null && goodsSign.GoodsBrandDate__c.month() == nowMonth) {
tempGoodsExportObject.goodsBrandColor = bgColor;
}
if(goodsSign.GoodsPriceDate__c != null && goodsSign.GoodsPriceDate__c.month() == nowMonth) {
tempGoodsExportObject.goodsPriceColor = bgColor;
}
if(goodsSign.GoodsCostPriceDate__c != null && goodsSign.GoodsCostPriceDate__c.month() == nowMonth) {
tempGoodsExportObject.goodsCostPriceColor = bgColor;
}
if(goodsSign.GoodsDescribeDate__c != null && goodsSign.GoodsDescribeDate__c.month() == nowMonth) {
tempGoodsExportObject.goodsDescribeColor = bgColor;
}
}
exportGoodsList.add(tempGoodsExportObject);
} return new PageReference('/apex/ExportGoodsPage');
} }

4.新建显示的VF页面

IsExportPage.page:此VF页面用于显示一个按钮,当点击按钮后,执行Excel生成操作。

 <apex:page controller="ExportGoodsController">
<apex:form >
<apex:commandButton action="{!exportGoods}" value="exportGoods"/>
</apex:form>
</apex:page>

ExportGoodsPage:生成Excel

 <apex:page controller="ExportGoodsController" cache="true" contenttype="application/x-excel# GenExcel.xls" showheader="false">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
</head>
<apex:dataTable value="{!exportGoodsList}" var="exportGoods" border="1">
<apex:column style="background:{!exportGoods.goodsNameColor};">
<apex:facet name="header">Goods Name </apex:facet>
{!exportGoods.goodsName}
</apex:column>
<apex:column style="background:{!exportGoods.goodsBrandColor};">
<apex:facet name="header">Goods Brand</apex:facet>
{!exportGoods.goodsBrand}
</apex:column>
<apex:column style="background:{!exportGoods.goodsPriceColor};">
<apex:facet name="header">Goods Price</apex:facet>
{!exportGoods.goodsPrice}
</apex:column>
<apex:column style="background:{!exportGoods.goodsCostPriceColor};">
<apex:facet name="header">Goods Cost Price</apex:facet>
{!exportGoods.goodsCostPrice}
</apex:column>
<apex:column style="background:{!exportGoods.goodsDescribeColor};">
<apex:facet name="header">Goods Describe</apex:facet>
{!exportGoods.goodsDescribe}
</apex:column>
</apex:dataTable>
</apex:page>

5.配置Button,并显示到列表页面上。

结果样式显示:

点击Goods Reports按钮,跳转到导出 记录的按钮页面

点击exportGoods则可以生成Excel。以下为Excel的生成界面,其中红色为修改的记录字段。

总结:上述demo只是演示当字段Tracking超过20个需要额外创建表的情况处理,当小于20个情况下可以直接通过History的表进行查询,有兴趣的可以自己尝试,生成页面因为使用DataTable,所以对于导出的记录行数有要求,必须不大于1000条,超过则应该会报Error。篇中如果有写的错误的地方欢迎指出,如果有疑问地方欢迎留言,转载请注明出处。

salesforce 零基础学习(二十三)数据记录导出至excel(自定义报表导出)的更多相关文章

  1. salesforce零基础学习(八十二)审批邮件获取最终审批人和审批意见

    项目中,审批操作无处不在.配置审批流时,我们有时候会用到queue,related user设置当前步骤的审批人,审批人可以一个或者多个.当审批人有多个时,邮件中获取当前记录的审批人和审批意见就不能随 ...

  2. salesforce 零基础学习(五十二)Trigger使用篇(二)

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

  3. salesforce零基础学习(一百一十三)Trigger中获取IP地址的过程

    本篇参考: https://developer.salesforce.com/docs/atlas.en-us.228.0.apexcode.meta/apexcode/apex_class_Auth ...

  4. salesforce 零基础学习(六十八)http callout test class写法

    此篇可以参考: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restfu ...

  5. 【转】【Salesforce】salesforce 零基础学习(十七)Trigger用法

    看本篇之前可以相应阅读以下Trigger相关文章: 1.https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigge ...

  6. salesforce零基础学习(一百零五)Change Data Capture

    本篇参考: https://developer.salesforce.com/docs/atlas.en-us.232.0.api_streaming.meta/api_streaming/using ...

  7. salesforce零基础学习(八十)使用autoComplete 输入内容自动联想结果以及去重实现

    项目中,我们有时候会需要实现自动联想功能,比如我们想输入用户或者联系人名称,去联想出系统中有的相关的用户和联系人,当点击以后获取相关的邮箱或者其他信息等等.这种情况下可以使用jquery ui中的au ...

  8. salesforce零基础学习(八十七)Apex 中Picklist类型通过Control 字段值获取Dependent List 值

    注:本篇解决方案内容实现转自:http://mysalesforceescapade.blogspot.com/2015/03/getting-dependent-picklist-values-fr ...

  9. salesforce零基础学习(八十九)使用 input type=file 以及RemoteAction方式上传附件

    在classic环境中,salesforce提供了<apex:inputFile>标签用来实现附件的上传以及内容获取.salesforce 零基础学习(二十四)解析csv格式内容中有类似的 ...

  10. salesforce零基础学习(九十六)Platform Event浅谈

    本篇参考:https://developer.salesforce.com/blogs/2018/07/which-streaming-event-do-i-use.html https://trai ...

随机推荐

  1. mac下svn问题——“.a”(静态库)文件无法上传解决

    mac下svn问题——“.a”(静态库)文件无法上传解决    “.a”(静态库)文件无法上传(svn工具:Versions)          网上查询了一下,说是Xcode自带的svn和Versi ...

  2. 我与solr(三)--solr后台相关介绍

    1.DashBoard: 介绍了当前solr的相关信息,运行时间,版本信息,java虚拟机的配置信息. 注意我们的solr与lucence的版本号是保持一致的,而不同的lucence版本也需要对应的j ...

  3. JS动态级联菜单

    JS动态级联菜单是前端常用的一个功能,特此抽时间研究了下,附上代码 <html> <head> <meta charset="utf-8" /> ...

  4. iOS开发流程总结

    本文由社区会员umyueyue分享 以下是会员umyueue总结的iOS开发流程以及学习中的资料分享. 流程:注册.开发.真机测试.发布以及上线. iPhone iOS 4从注册到app上线开发流程 ...

  5. 基于AutoCAD的ObjectARX之NET扩展(mcnetarx)-AcdbEntGet

    1.AcdbEntGet用于获取实体的组码. 示例: ' 定义保存实体名称的变量 Dim ent() As Integer = New Integer() {} ' 获取最后一个实体 mcnetarx ...

  6. 有关google的小问题

    从本周的开始,突然发现自己的浏览器打不开google了,我的默认浏览器是qq的,不过使用的也是google搜索.刚开始我以为是浏览器中毒了,就重新下了一个,结果还是同样的情况,而且我使用搜狗或者百度的 ...

  7. C++ 非阻塞套接字的使用 (3)

    异步非阻塞套接字避免了死循环的接收问题,但是软件用起来体验还是很差.究其原因,软件在指令的发送.接收上, 采取了一种不合理的方式:在指令的发送后,立刻调用接收函数,等待回令. 若是采用同步阻塞套接字, ...

  8. xml note

          10. 两种不同的XML分析模型: DOM:所有数据位于内存 SAX:流程性分析,不必把所有数据Load到内存中,可分析大型的XML文件,常用于Servlet-side的XML-xhtml ...

  9. 练习1-16:修改打印最长文本行的程序的主程序main,使之可以打印任意长度的输入行的长度,并尽可能多地打印文本(C程序设计语言 第2版)

    该书英文配套答案 Answer to Exercise -, page Revise the main routine of the longest-line program so it will c ...

  10. [ASE]项目介绍及项目跟进——TANK BATTLE·INFINITE

    童年的记忆,大概是每周末和小伙伴们围坐在电视机前,在20来寸的电视机屏幕里守卫着这个至今都不知道是什么的白色大鸟. 当年被打爆的坦克数量估计也能绕地球个三两圈了吧. 十几年过去了,游戏从2D-3D,画 ...