我们都知道,报表有个功能为导出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代码如下所示:
1 trigger GoodsTrigger on Goods__c (before delete, before update) {
2 if(trigger.isBefore) {
3 //get the Schema Information of GoodsSign to check whether current user has access to operate the data
4 Schema.DescribeSObjectResult goodsSignDescribe = GoodsSign__c.sObjectType.getDescribe();
5 List<GoodsSign__c> goodsSignList = new List<GoodsSign__c>();
6 //第一个参数为Goods__c的ID,第二个参数为GoodsSign__c
7 Map<ID,GoodsSign__c> goodsIdToGoodsSignMap = new Map<ID,GoodsSign__c>();
8 if(trigger.isUpdate) {
9 Set<ID> goodsIdSet = new Set<ID>();
10 if(goodsSignDescribe.isCreateable() && goodsSignDescribe.isUpdateable()) {
11 List<Goods__c> goodsNewList = trigger.new;
12 List<Goods__c> goodsOldList = trigger.old;
13 for(Goods__c goods : goodsNewList) {
14 goodsIdSet.add(goods.Id);
15 }
16 String fetchGoodsSignByGoodsId = 'SELECT CreatedById, CreatedDate,' +
17 ' IsDeleted, GoodsBrandDate__c, GoodsCostPriceDate__c,' +
18 ' GoodsDescribeDate__c, GoodsId__c, GoodsNameDate__c,' +
19 ' GoodsPriceDate__c, Name, LastModifiedById, LastModifiedDate,' +
20 ' OwnerId, Id, SystemModstamp FROM GoodsSign__c ' +
21 ' where GoodsId__c in :goodsIdSet';
22 List<GoodsSign__c> tempGoodsSignList = Database.query(fetchGoodsSignByGoodsId);
23 for(GoodsSign__c goodsSign : tempGoodsSignList) {
24 goodsIdToGoodsSignMap.put(goodsSign.GoodsId__c,goodsSign);
25 }
26 for(Integer i=0;i<goodsNewList.size();i++) {
27 Goods__c goodsNew = goodsNewList.get(i);
28 Goods__c goodsOld = goodsOldList.get(i);
29 GoodsSign__c goodsSign = new GoodsSign__c();
30 Id goodsId = goodsNew.Id;
31 if(goodsIdToGoodsSignMap.get(goodsId) != null) {
32 goodsSign = goodsIdToGoodsSignMap.get(goodsId);
33 }
34
35 if(goodsNew.GoodsName__c != goodsOld.GoodsName__c) {
36 goodsSign.GoodsNameDate__c = System.today();
37 }
38 if(goodsNew.GoodsPrice__c != goodsOld.GoodsPrice__c) {
39 goodsSign.GoodsPriceDate__c = System.today();
40 }
41 if(goodsNew.GoodsCostPrice__c != goodsOld.GoodsCostPrice__c) {
42 goodsSign.GoodsCostPriceDate__c = System.today();
43 }
44 if(goodsNew.GoodsBrand__c != goodsOld.GoodsBrand__c) {
45 goodsSign.GoodsBrandDate__c = System.today();
46 }
47 if(goodsNew.GoodsDescribe__c != goodsOld.GoodsDescribe__c) {
48 goodsSign.GoodsDescribeDate__c = System.today();
49 }
50 if(goodsSign.GoodsId__c == null) {
51 goodsSign.GoodsId__c = goodsId;
52 }
53 goodsSignList.add(goodsSign);
54 }
55 if(goodsSignList.size() > 0) {
56 upsert goodsSignList;
57 }
58 }
59
60 } else if(trigger.isDelete) {
61 //cascade delete
62 if(goodsSignDescribe.isDeletable()) {
63 List<Goods__c> goodsList = trigger.old;
64 Set<ID> goodsIdSet = new Set<ID>();
65 for(Goods__c currentGoods : goodsList) {
66 if(!goodsIdSet.contains(currentGoods.Id)) {
67 goodsIdSet.add(currentGoods.Id);
68 }
69 }
70 String fetchGoodsSignByGoodsIdSet = 'SELECT CreatedById, CreatedDate,' +
71 ' IsDeleted, GoodsBrandDate__c, GoodsCostPriceDate__c, GoodsDescribeDate__c,' +
72 ' GoodsId__c, GoodsNameDate__c, GoodsPriceDate__c, Name, LastModifiedById,' +
73 ' LastModifiedDate, OwnerId, Id, SystemModstamp FROM GoodsSign__c' +
74 ' where GoodsId__c in :goodsIdSet';
75 List<GoodsSign__c> goodsSignNeedDeleteList = Database.query(fetchGoodsSignByGoodsIdSet);
76 delete goodsSignNeedDeleteList;
77 }
78 }
79 }
80 }
此Trigger有两个功能:
1.当进行修改操作并且Goods__c记录有字段改变时,如果有相对应的GoodsSign__c进行对应,则update此记录,否则新建记录,并记录哪些字段有修改;
2.当进行删除操作时,如果有相对应的GoodsSign__c进行对应,则级联删除。
两者操作均需要当前用户有GoodsSign的操作权限。
2.新建一个类用来记录导出的字段以及导出的字段的颜色。
1 public with sharing class GoodsExportObject {
2 public String goodsName{get;set;}
3 public String goodsNameColor{get;set;}
4 public String goodsBrand{get;set;}
5 public String goodsBrandColor{get;set;}
6 public String goodsPrice{get;set;}
7 public String goodsPriceColor{get;set;}
8 public String goodsCostPrice{get;set;}
9 public String goodsCostPriceColor{get;set;}
10 public String goodsDescribe{get;set;}
11 public String goodsDescribeColor{get;set;}
12 }
3.新建Controller,此Controller用来获取显示到excel的数据。
1 public with sharing class ExportGoodsController {
2
3 List<Goods__c> goodsList{get;set;}
4 List<GoodsSign__c> goodsSignList{get;set;}
5 public List<GoodsExportObject> exportGoodsList{get;set;}
6 Map<Id,GoodsSign__c> goodsSignMap = new Map<Id,GoodsSign__c>();
7 public ExportGoodsController() {
8 goodsList = new List<Goods__c>();
9 goodsSignList = new List<GoodsSign__c>();
10 exportGoodsList = new List<GoodsExportObject>();
11 String fetchAllGoods = 'SELECT CreatedById, CreatedDate, IsDeleted,' +
12 ' Name, GoodsBrand__c, GoodsCostPrice__c, GoodsDescribe__c, GoodsName__c,' +
13 ' GoodsPrice__c, LastActivityDate, LastModifiedById, LastModifiedDate,' +
14 ' OwnerId, Id, SystemModstamp FROM Goods__c';
15 goodsList = Database.query(fetchAllGoods);
16 String fetchAllGoodsSign = 'SELECT CreatedById, CreatedDate, IsDeleted,' +
17 ' GoodsBrandDate__c, GoodsCostPriceDate__c, GoodsDescribeDate__c,' +
18 ' GoodsId__c, GoodsNameDate__c, GoodsPriceDate__c, Name, LastModifiedById,' +
19 ' LastModifiedDate, OwnerId, Id, SystemModstamp FROM GoodsSign__c';
20 goodsSignList = Database.query(fetchAllGoodsSign);
21 for(GoodsSign__c goodsSign : goodsSignList) {
22 if(!goodsSignMap.containsKey(goodsSign.GoodsId__c)) {
23 goodsSignMap.put(goodsSign.GoodsId__c,goodsSign);
24 }
25 }
26 }
27
28
29 public PageReference exportGoods() {
30 String bgColor = 'red';
31 Integer nowMonth = System.today().month();
32 for(Goods__c currentGoods : goodsList) {
33 GoodsSign__c goodsSign = goodsSignMap.get(currentGoods.Id);
34 GoodsExportObject tempGoodsExportObject = new GoodsExportObject();
35 tempGoodsExportObject.goodsName = currentGoods.GoodsName__c;
36 tempGoodsExportObject.goodsBrand = currentGoods.GoodsBrand__c;
37 tempGoodsExportObject.goodsPrice = String.valueOf(currentGoods.GoodsPrice__c);
38 tempGoodsExportObject.goodsCostPrice = String.valueOf(currentGoods.GoodsCostPrice__c);
39 tempGoodsExportObject.goodsDescribe = currentGoods.GoodsDescribe__c;
40 if(goodsSign != null) {
41 if(goodsSign.GoodsNameDate__c != null && goodsSign.GoodsNameDate__c.month() == nowMonth) {
42 tempGoodsExportObject.goodsNameColor = bgColor;
43 }
44 if(goodsSign.GoodsBrandDate__c != null && goodsSign.GoodsBrandDate__c.month() == nowMonth) {
45 tempGoodsExportObject.goodsBrandColor = bgColor;
46 }
47 if(goodsSign.GoodsPriceDate__c != null && goodsSign.GoodsPriceDate__c.month() == nowMonth) {
48 tempGoodsExportObject.goodsPriceColor = bgColor;
49 }
50 if(goodsSign.GoodsCostPriceDate__c != null && goodsSign.GoodsCostPriceDate__c.month() == nowMonth) {
51 tempGoodsExportObject.goodsCostPriceColor = bgColor;
52 }
53 if(goodsSign.GoodsDescribeDate__c != null && goodsSign.GoodsDescribeDate__c.month() == nowMonth) {
54 tempGoodsExportObject.goodsDescribeColor = bgColor;
55 }
56 }
57 exportGoodsList.add(tempGoodsExportObject);
58 }
59
60 return new PageReference('/apex/ExportGoodsPage');
61 }
62
63
64
65
66 }
4.新建显示的VF页面
IsExportPage.page:此VF页面用于显示一个按钮,当点击按钮后,执行Excel生成操作。
1 <apex:page controller="ExportGoodsController">
2 <apex:form >
3 <apex:commandButton action="{!exportGoods}" value="exportGoods"/>
4 </apex:form>
5 </apex:page>
ExportGoodsPage:生成Excel
1 <apex:page controller="ExportGoodsController" cache="true" contenttype="application/x-excel# GenExcel.xls" showheader="false">
2 <head>
3 <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
4 </head>
5 <apex:dataTable value="{!exportGoodsList}" var="exportGoods" border="1">
6 <apex:column style="background:{!exportGoods.goodsNameColor};">
7 <apex:facet name="header">Goods Name </apex:facet>
8 {!exportGoods.goodsName}
9 </apex:column>
10 <apex:column style="background:{!exportGoods.goodsBrandColor};">
11 <apex:facet name="header">Goods Brand</apex:facet>
12 {!exportGoods.goodsBrand}
13 </apex:column>
14 <apex:column style="background:{!exportGoods.goodsPriceColor};">
15 <apex:facet name="header">Goods Price</apex:facet>
16 {!exportGoods.goodsPrice}
17 </apex:column>
18 <apex:column style="background:{!exportGoods.goodsCostPriceColor};">
19 <apex:facet name="header">Goods Cost Price</apex:facet>
20 {!exportGoods.goodsCostPrice}
21 </apex:column>
22 <apex:column style="background:{!exportGoods.goodsDescribeColor};">
23 <apex:facet name="header">Goods Describe</apex:facet>
24 {!exportGoods.goodsDescribe}
25 </apex:column>
26 </apex:dataTable>
27 </apex:page>
5.配置Button,并显示到列表页面上。
结果样式显示:
点击Goods Reports按钮,跳转到导出 记录的按钮页面
点击exportGoods则可以生成Excel。以下为Excel的生成界面,其中红色为修改的记录字段。
总结:上述demo只是演示当字段Tracking超过20个需要额外创建表的情况处理,当小于20个情况下可以直接通过History的表进行查询,有兴趣的可以自己尝试,生成页面因为使用DataTable,所以对于导出的记录行数有要求,必须不大于1000条,超过则应该会报Error。篇中如果有写的错误的地方欢迎指出,如果有疑问地方欢迎留言,转载请注明出处。