ExportExcel.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. unit ExportExcel;
  2. interface
  3. uses
  4. Classes, ZjGrid, ScXlsOutput, ScXlsCustomUD, Windows, StdCtrls,
  5. sdIDTree, sdDB, Graphics, SysUtils, ProgressHintFrm, Forms, Controls;
  6. type
  7. TExcelExportor = class
  8. private
  9. FXlsOutPut: TXlsOutPut;
  10. FGrid: TZJGrid;
  11. FTempFile: string;
  12. FFileName: string;
  13. procedure InitialPage(AGrid: TZJGrid; AXlsPage: TXlsCustomPage);
  14. protected
  15. procedure BeforeExport;
  16. procedure EndExport;
  17. public
  18. constructor Create;
  19. destructor Destroy; override;
  20. procedure ExportToXlsPage(AGrid: TZJGrid; AXlsPage: TXlsCustomPage);
  21. procedure ExportToFile(AGrid: TZJGrid; const AFileName: string);
  22. end;
  23. PColInfo = ^TColInfo;
  24. TColInfo = record
  25. // 字段名
  26. FieldName: string;
  27. // 查询字段名,应用于联合几个数据库的情况,参照dataset的Lookup方式
  28. KeyField: string;
  29. LookupKeyField: string;
  30. // 查询数据库ID
  31. LookupDataSetIndex: Integer;
  32. // 列名
  33. TitleCaption: string;
  34. // 列宽
  35. Width: Integer;
  36. // 对齐方式
  37. HorTextAlign: TUDHTextAlign;
  38. //VerTextAlign: TUDVTextAlign;
  39. end;
  40. PColInfos = ^TColInfos;
  41. TColInfos = array [0..30] of TColInfo;
  42. // 仿照DataSet的Lookup以及数据库的AutoUpdate已达到关于sdIDTree导出数据至Excel的普适性
  43. // 导出前须根据所需列信息,以及查询数据库(列信息须与查询数据库对等,否则将会报错,并不检查列与数据库是否匹配)
  44. TIDTreeExcelExportor = class
  45. private
  46. FXlsOutPut: TXlsOutPut;
  47. FDataSetList: TList;
  48. FColInfos: PColInfos;
  49. FColCount: Integer;
  50. FHasLevelCode: Boolean;
  51. FTree: TsdIDTree;
  52. FTempFile: string;
  53. // 当清单数超过3w3k行时,使用Variant会内存溢出
  54. function GetCellValue(ANode: TsdIDTreeNode; ColInfo: TColInfo): Variant;
  55. // 故换成直接使用String
  56. function GetCellStr(ANode: TsdIDTreeNode; ColInfo: TColInfo): string;
  57. procedure ExportNodeData(ANode: TsdIDTreeNode; AXlsPage: TXlsCustomPage; const ALevelCode: string);
  58. procedure ExportTreeNode(ANode: TsdIDTreeNode; AXlsPage: TXlsCustomPage; const ALevelCode: string);
  59. procedure DefineHeader(AXlsPage: TXlsCustomPage);
  60. protected
  61. procedure BeforeExport;
  62. procedure EndExport;
  63. public
  64. constructor Create;
  65. destructor Destroy; override;
  66. procedure AddLookupDataSet(ADataSet: TsdDataSet);
  67. procedure DefineCol(AColInfos: PColInfos; AColCount: Integer);
  68. procedure ExportToXlsPage(ATree: TsdIDTree; AXlsPage: TXlsCustomPage);
  69. procedure ExportToFile(ATree: TsdIDTree; const AFileName: string);
  70. property HasLevelCode: Boolean read FHasLevelCode write FHasLevelCode;
  71. end;
  72. const
  73. ciLedger: array [0..8] of TColInfo =(
  74. (FieldName: 'Code'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '项目节编号'; Width: 120; HorTextAlign: htaLeft),
  75. (FieldName: 'B_Code'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '清单编号'; Width: 80; HorTextAlign: htaLeft),
  76. (FieldName: 'Name'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '名称'; Width: 200; HorTextAlign: htaLeft),
  77. (FieldName: 'Units'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '单位'; Width: 40; HorTextAlign: htaCenter),
  78. (FieldName: 'Price'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '单价'; Width: 60; HorTextAlign: htaRight),
  79. (FieldName: 'OrgQuantity'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '数量'; Width: 80; HorTextAlign: htaRight),
  80. (FieldName: 'OrgTotalPrice'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '金额'; Width: 80; HorTextAlign: htaRight),
  81. (FieldName: 'DrawingCode'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '图号'; Width: 100; HorTextAlign: htaLeft),
  82. (FieldName: 'MemoStr'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '备注'; Width: 80; HorTextAlign: htaLeft)
  83. );
  84. ciFxBills: array [0..10] of TColInfo =(
  85. (FieldName: 'Code'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '预算项目节'; Width: 120; HorTextAlign: htaLeft),
  86. (FieldName: 'B_Code'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '清单子目号'; Width: 80; HorTextAlign: htaLeft),
  87. (FieldName: 'Name'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '名称'; Width: 200; HorTextAlign: htaLeft),
  88. (FieldName: 'Units'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '单位'; Width: 40; HorTextAlign: htaCenter),
  89. (FieldName: 'OrgQuantity'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '清单数量'; Width: 80; HorTextAlign: htaRight),
  90. (FieldName: 'DgnQuantity1'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '设计数量1'; Width: 80; HorTextAlign: htaRight),
  91. (FieldName: 'DgnQuantity2'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '设计数量2'; Width: 80; HorTextAlign: htaRight),
  92. (FieldName: 'Price'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '单价'; Width: 60; HorTextAlign: htaRight),
  93. (FieldName: 'OrgTotalPrice'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '合价'; Width: 80; HorTextAlign: htaRight),
  94. (FieldName: 'DrawingCode'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '图号'; Width: 100; HorTextAlign: htaLeft),
  95. (FieldName: 'MemoStr'; KeyField: ''; LookupKeyField: ''; LookupDataSetIndex: -1; TitleCaption: '备注'; Width: 80; HorTextAlign: htaLeft)
  96. );
  97. implementation
  98. uses
  99. ZhAPI, Variants, UtilMethods;
  100. { TExcelExportor }
  101. procedure TExcelExportor.BeforeExport;
  102. begin
  103. Screen.Cursor := crHourGlass;
  104. ShowProgressHint('导出Excel表格数据', FGrid.RowCount);
  105. end;
  106. constructor TExcelExportor.Create;
  107. begin
  108. FXlsOutPut := TXlsOutPut.Create;
  109. FTempFile := GetTempFileName;
  110. end;
  111. destructor TExcelExportor.Destroy;
  112. begin
  113. if FileExists(FTempFile) then
  114. DeleteFileOrFolder(FTempFile);
  115. FXlsOutPut.Free;
  116. inherited;
  117. end;
  118. procedure TExcelExportor.EndExport;
  119. begin
  120. CloseProgressHint;
  121. Screen.Cursor := crDefault;
  122. end;
  123. procedure TExcelExportor.ExportToFile(AGrid: TZJGrid;
  124. const AFileName: string);
  125. begin
  126. FFileName := AFileName;
  127. FGrid := AGrid;
  128. BeforeExport;
  129. try
  130. ExportToXlsPage(AGrid, FXlsOutPut.AddPage);
  131. FXlsOutPut.SaveToFile(FTempFile);
  132. if not FileExists(FFileName) or QuestMessage('存在同名文件,是否替换?') then
  133. CopyFile(PChar(FTempFile), PChar(FFileName), False);
  134. finally
  135. EndExport;
  136. end;
  137. end;
  138. procedure TExcelExportor.ExportToXlsPage(AGrid: TZJGrid;
  139. AXlsPage: TXlsCustomPage);
  140. procedure SetXlsCellTextAlign(AXlsCell: TXlsCustomCell; AGridCell: TzjCell);
  141. begin
  142. case AGridCell.TextAlign of
  143. gaTopLeft:
  144. begin
  145. AXlsCell.VTextAlign := vtaTop;
  146. AXlsCell.HTextAlign := htaLeft;
  147. end;
  148. gaTopCenter:
  149. begin
  150. AXlsCell.VTextAlign := vtaTop;
  151. AXlsCell.HTextAlign := htaCenter;
  152. end;
  153. gaTopRight:
  154. begin
  155. AXlsCell.VTextAlign := vtaTop;
  156. AXlsCell.HTextAlign := htaRight;
  157. end;
  158. gaCenterLeft:
  159. begin
  160. AXlsCell.VTextAlign := vtaCenter;
  161. AXlsCell.HTextAlign := htaLeft;
  162. end;
  163. gaCenterCenter:
  164. begin
  165. AXlsCell.VTextAlign := vtaCenter;
  166. AXlsCell.HTextAlign := htaCenter;
  167. end;
  168. gaCenterRight:
  169. begin
  170. AXlsCell.VTextAlign := vtaCenter;
  171. AXlsCell.HTextAlign := htaRight;
  172. end;
  173. gaBottomLeft:
  174. begin
  175. AXlsCell.VTextAlign := vtaBottom;
  176. AXlsCell.HTextAlign := htaLeft;
  177. end;
  178. gaBottomCenter:
  179. begin
  180. AXlsCell.VTextAlign := vtaBottom;
  181. AXlsCell.HTextAlign := htaCenter;
  182. end;
  183. gaBottomRight:
  184. begin
  185. AXlsCell.VTextAlign := vtaBottom;
  186. AXlsCell.HTextAlign := htaRight;
  187. end;
  188. end;
  189. if goWarpText in AGridCell.Grid.Options then
  190. AXlsCell.WartText := True;
  191. end;
  192. procedure ExportGridCell(AGridCell: TzjCell);
  193. var
  194. XlsCell: TXlsCustomCell;
  195. begin
  196. if AGridCell = nil then Exit;
  197. XlsCell := AXlsPage.AddCell(AGridCell.Col, AGridCell.Row, AGridCell.Text);
  198. SetXlsCellTextAlign(XlsCell, AGridCell);
  199. XlsCell.Font.Name := AGridCell.Font.Name;
  200. XlsCell.Font.Size := AGridCell.Font.Size;
  201. XlsCell.Width := AGridCell.Width;
  202. XlsCell.Height := AGridCell.Height;
  203. end;
  204. var
  205. iColumn, iRow: Integer;
  206. begin
  207. InitialPage(AGrid, AXlsPage);
  208. for iRow := 0 to AGrid.RowCount - 1 do
  209. begin
  210. UpdateProgressHint(Format('导出第%d行数据', [iRow + 1]));
  211. UpdateProgressHint(1);
  212. for iColumn := 0 to AGrid.ColCount - 1 do
  213. ExportGridCell(AGrid.Cells[iColumn, iRow]);
  214. end;
  215. end;
  216. procedure TExcelExportor.InitialPage(AGrid: TZJGrid;
  217. AXlsPage: TXlsCustomPage);
  218. procedure InitialColumnWidth;
  219. var
  220. iColumn: Integer;
  221. begin
  222. for iColumn := 0 to AGrid.ColCount - 1 do
  223. AXlsPage.Widths[iColumn] := AGrid.ColWidths[iColumn];
  224. end;
  225. procedure InitialRowHeight;
  226. var
  227. iRow: Integer;
  228. begin
  229. for iRow := 0 to AGrid.RowCount - 1 do
  230. AXlsPage.Heights.Items[iRow] := AGrid.RowHeights[iRow];
  231. end;
  232. begin
  233. InitialColumnWidth;
  234. InitialRowHeight;
  235. end;
  236. { TIDTreeExcelExportor }
  237. constructor TIDTreeExcelExportor.Create;
  238. begin
  239. FXlsOutPut := TXlsOutPut.Create;
  240. FDataSetList := TList.Create;
  241. FTempFile := GetTempFileName;
  242. end;
  243. destructor TIDTreeExcelExportor.Destroy;
  244. begin
  245. if FileExists(FTempFile) then
  246. DeleteFileOrFolder(FTempFile);
  247. FDataSetList.Free;
  248. FXlsOutPut.Free;
  249. inherited;
  250. end;
  251. procedure TIDTreeExcelExportor.ExportToFile(ATree: TsdIDTree;
  252. const AFileName: string);
  253. begin
  254. FTree := ATree;
  255. BeforeExport;
  256. try
  257. ExportToXlsPage(ATree, FXlsOutPut.AddPage);
  258. UpdateProgressHint('保存0号台账Excel数据');
  259. FXlsOutPut.SaveToFile(FTempFile);
  260. if not FileExists(AFileName) or QuestMessage('存在同名文件,是否替换?') then
  261. CopyFile(PChar(FTempFile), PChar(AFileName), False);
  262. finally
  263. EndExport;
  264. end;
  265. end;
  266. procedure TIDTreeExcelExportor.ExportTreeNode(ANode: TsdIDTreeNode;
  267. AXlsPage: TXlsCustomPage; const ALevelCode: string);
  268. function GetFirstChildLevelCode(const ACode: string): string;
  269. begin
  270. Result := ACode + '.1';
  271. end;
  272. function GetNextSiblingLevelCode(const ACode: string): string;
  273. var
  274. strPreCode, strLastCode: string;
  275. iNextCode: Integer;
  276. begin
  277. if Pos('.', ACode) = 0 then
  278. Result := IntToStr(StrToIntDef(ACode, 1) + 1)
  279. else
  280. begin
  281. strPreCode := GetPrefixOfCode(ACode, '.');
  282. strLastCode := GetLastSetmentOfCode(ACode, '.');
  283. iNextCode := StrToIntDef(strLastCode, 1) + 1;
  284. Result := strPreCode + '.' + IntToStr(iNextCode);
  285. end;
  286. end;
  287. var
  288. sHint: string;
  289. begin
  290. if not Assigned(ANode) then Exit;
  291. if ANode.Rec.ValueByName('Code').AsString <> '' then
  292. sHint := '正在导出清单 ' + ANode.Rec.ValueByName('Code').AsString
  293. else if ANode.Rec.ValueByName('B_Code').AsString <> '' then
  294. sHint := '正在导出清单 ' + ANode.Rec.ValueByName('B_Code').AsString
  295. else
  296. sHint := '正在导出清单 ' + ANode.Rec.ValueByName('Name').AsString;
  297. UpdateProgressHint(sHint);
  298. UpdateProgressHint(1);
  299. ExportNodeData(ANode, AXlsPage, ALevelCode);
  300. ExportTreeNode(ANode.FirstChild, AXlsPage, GetFirstChildLevelCode(ALevelCode));
  301. ExportTreeNode(ANode.NextSibling, AXlsPage, GetNextSiblingLevelCode(ALevelCode));
  302. end;
  303. procedure TIDTreeExcelExportor.ExportNodeData(ANode: TsdIDTreeNode;
  304. AXlsPage: TXlsCustomPage; const ALevelCode: string);
  305. function ExportCell(ACol, ARow: Integer; AValue: Variant): TXlsCustomCell;
  306. begin
  307. Result := nil;
  308. // -----------
  309. if VarIsNull(AValue) then Exit;
  310. // -----------
  311. // 当数据超过3w3k行时,运行至某行时,AddCell会内存溢出
  312. // 可能是Cell的数目超过某个限度时,报错
  313. // 如果AValue为Null时不AddCell,则3w3k行可以安全度过
  314. case VarType(AValue) of
  315. varSmallInt, varInteger, varSingle, varDouble,
  316. varCurrency, varShortInt, varByte, varWord,
  317. varLongWord, varInt64:
  318. begin
  319. if AValue <> 0 then
  320. Result := AXlsPage.AddCell(ACol, ARow, AValue);
  321. end
  322. else Result := AXlsPage.AddCell(ACol, ARow, AValue);
  323. end;
  324. end;
  325. var
  326. iCol: Integer;
  327. ColInfo: TColInfo;
  328. XlsCell: TXlsCustomCell;
  329. sStr: string;
  330. begin
  331. if not Assigned(ANode) then Exit;
  332. for iCol := 0 to FColCount - 1 do
  333. begin
  334. ColInfo := FColInfos[iCol];
  335. XlsCell := ExportCell(iCol, ANode.MajorIndex + 1, GetCellValue(ANode, ColInfo));
  336. {sStr := GetCellStr(ANode, ColInfo);
  337. if sStr = '' then Continue;
  338. XlsCell := AXlsPage.AddCell(iCol, ANode.MajorIndex + 1, sStr);}
  339. if Assigned(XlsCell) then
  340. begin
  341. XlsCell.HTextAlign := ColInfo.HorTextAlign;
  342. //XlsCell.VTextAlign := ColInfo.VerTextAlign;
  343. XlsCell.Font.Name := 'SmartSimSun';
  344. XlsCell.Font.Size := 9;
  345. end;
  346. end;
  347. if HasLevelCode then
  348. begin
  349. XlsCell := ExportCell(FColCount, ANode.MajorIndex + 1, ALevelCode);
  350. XlsCell.Font.Name := 'SmartSimSun';
  351. XlsCell.Font.Size := 9;
  352. end;
  353. end;
  354. procedure TIDTreeExcelExportor.ExportToXlsPage(ATree: TsdIDTree;
  355. AXlsPage: TXlsCustomPage);
  356. begin
  357. DefineHeader(AXlsPage);
  358. ExportTreeNode(ATree.FirstNode, AXlsPage, '1');
  359. end;
  360. function TIDTreeExcelExportor.GetCellValue(ANode: TsdIDTreeNode;
  361. ColInfo: TColInfo): Variant;
  362. function GetRec: TsdDataRecord;
  363. var
  364. DataSet: TsdDataSet;
  365. begin
  366. Result := nil;
  367. DataSet := TsdDataSet(FDataSetList.Items[ColInfo.LookupDataSetIndex]);
  368. if not Assigned(DataSet) then Exit;
  369. Result := DataSet.Locate(ColInfo.LookupKeyField, ANode.Rec.ValueByName(ColInfo.KeyField).Value);
  370. end;
  371. var
  372. ARec: TsdDataRecord;
  373. begin
  374. Result := '';
  375. if not Assigned(ANode) then Exit;
  376. if ColInfo.LookupDataSetIndex = -1 then
  377. ARec := ANode.Rec
  378. else
  379. ARec := GetRec;
  380. if Assigned(ARec) then
  381. Result := ARec.ValueByName(ColInfo.FieldName).Value;
  382. end;
  383. procedure TIDTreeExcelExportor.DefineHeader(AXlsPage: TXlsCustomPage);
  384. var
  385. iCol: Integer;
  386. ColInfo: TColInfo;
  387. XlsCell: TXlsCustomCell;
  388. begin
  389. for iCol := 0 to FColCount - 1 do
  390. begin
  391. ColInfo := FColInfos[iCol];
  392. XlsCell := AXlsPage.AddCell(iCol, 0, ColInfo.TitleCaption);
  393. XlsCell.HTextAlign := htaCenter;
  394. XlsCell.Font.Name := '黑体';
  395. XlsCell.Font.Size := 10;
  396. XlsCell.Font.Style := [fsBold];
  397. AXlsPage.Widths[iCol] := ColInfo.Width;
  398. end;
  399. if HasLevelCode then
  400. begin
  401. XlsCell := AXlsPage.AddCell(iCol, 0, '层次编号');
  402. XlsCell.HTextAlign := htaCenter;
  403. XlsCell.Font.Name := '黑体';
  404. XlsCell.Font.Size := 10;
  405. XlsCell.Font.Style := [fsBold];
  406. end;
  407. end;
  408. procedure TIDTreeExcelExportor.AddLookupDataSet(ADataSet: TsdDataSet);
  409. begin
  410. FDataSetList.Add(ADataSet);
  411. end;
  412. procedure TIDTreeExcelExportor.DefineCol(AColInfos: PColInfos;
  413. AColCount: Integer);
  414. begin
  415. FColInfos := AColInfos;
  416. FColCount := AColCount;
  417. end;
  418. procedure TIDTreeExcelExportor.BeforeExport;
  419. begin
  420. Screen.Cursor := crHourGlass;
  421. ShowProgressHint('导出0号台账Excel数据', FTree.Count);
  422. end;
  423. procedure TIDTreeExcelExportor.EndExport;
  424. begin
  425. CloseProgressHint;
  426. Screen.Cursor := crDefault;
  427. end;
  428. function TIDTreeExcelExportor.GetCellStr(ANode: TsdIDTreeNode;
  429. ColInfo: TColInfo): string;
  430. function GetRec: TsdDataRecord;
  431. var
  432. DataSet: TsdDataSet;
  433. begin
  434. Result := nil;
  435. DataSet := TsdDataSet(FDataSetList.Items[ColInfo.LookupDataSetIndex]);
  436. if not Assigned(DataSet) then Exit;
  437. Result := DataSet.Locate(ColInfo.LookupKeyField, ANode.Rec.ValueByName(ColInfo.KeyField).Value);
  438. end;
  439. var
  440. ARec: TsdDataRecord;
  441. begin
  442. Result := '';
  443. if not Assigned(ANode) then Exit;
  444. if ColInfo.LookupDataSetIndex = -1 then
  445. ARec := ANode.Rec
  446. else
  447. ARec := GetRec;
  448. if Assigned(ARec) then
  449. Result := ARec.ValueByName(ColInfo.FieldName).AsString;
  450. if SameText(Result, '0') then
  451. Result := '';
  452. end;
  453. end.