unit StandardBillsDm; interface uses SysUtils, Classes, ADODB, DB, DBClient, Provider, sdIDTree, UtilMethods, sdDB, sdProvider; type TStandardBillsData = class(TDataModule) sdpStdBills: TsdADOProvider; sddStdBills: TsdDataSet; sdvStdBills: TsdDataView; private FBillsTree: TsdIDTree; procedure CreateBillsTree; procedure OpenConnetion(AConnection: TADOConnection); procedure ExpandFirstNode; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Open(AConnection: TADOConnection); procedure CalcCacheData(ANode:TsdIDTreeNode; AOrder: Integer; AParentPath: String); procedure RecursiveExportBillsJson(const AFileName: string); property BillsTree: TsdIDTree read FBillsTree; end; implementation {$R *.dfm} { TStandardBillsData } procedure TStandardBillsData.CalcCacheData(ANode: TsdIDTreeNode; AOrder: Integer; AParentPath: String); var sNodePath: string; begin if not Assigned(ANode) then Exit; ANode.Rec.ValueByName('BillsLevel').AsInteger := ANode.Level + 1; ANode.Rec.ValueByName('BillsOrder').AsInteger := AOrder; if AParentPath = '' then sNodePath := IntToStr(ANode.ID) else sNodePath := AParentPath + '.' + IntToStr(ANode.ID); ANode.Rec.ValueByName('FullPath').AsString := sNodePath; ANode.Rec.ValueByName('IsLeaf').AsBoolean := not ANode.HasChildren; CalcCacheData(ANode.FirstChild, 1, sNodePath); CalcCacheData(ANode.NextSibling, AOrder + 1, AParentPath); end; constructor TStandardBillsData.Create(AOwner: TComponent); begin inherited; CreateBillsTree; end; procedure TStandardBillsData.CreateBillsTree; begin FBillsTree := TsdIDTree.Create; FBillsTree.KeyFieldName := 'ID'; FBillsTree.ParentFieldName := 'ParentID'; FBillsTree.NextSiblingFieldName := 'NextID'; FBillsTree.AutoExpand := False; end; destructor TStandardBillsData.Destroy; begin FBillsTree.Free; inherited; end; procedure TStandardBillsData.ExpandFirstNode; begin if FBillsTree.FirstNode <> nil then FBillsTree.FirstNode.Expand; end; procedure TStandardBillsData.Open(AConnection: TADOConnection); begin OpenConnetion(AConnection); FBillsTree.DataView := sdvStdBills; ExpandFirstNode; end; procedure TStandardBillsData.OpenConnetion(AConnection: TADOConnection); begin sdpStdBills.Connection := AConnection; sdpStdBills.TableName := 'BillsTree'; sddStdBills.Open; sdvStdBills.Open; sddStdBills.AddIndex('idxID', 'ID'); sddStdBills.AddIndex('sdxCode', 'Code'); sddStdBills.AddIndex('sddBCode', 'B_Code'); end; procedure TStandardBillsData.RecursiveExportBillsJson( const AFileName: string); var sgs: TStrings; function GetNodeData(ANode: TsdIDTreeNode; AOrder: Integer; AFullPath: string): string; const sBillsJson = '"id": %d, "pid": %d, "order": %d, "full_path": "%s", "level": %d, "is_leaf" : %d, ' + '"code": "%s", "b_code": "%s", "name": "%s", "unit": "%s"'; begin Result := Format(sBillsJson, [ANode.ID, ANode.Rec.ValueByName('ParentID').AsInteger, AOrder, AFullPath, ANode.Rec.ValueByName('BillsLevel').AsInteger, Integer(not ANode.HasChildren), ANode.Rec.ValueByName('Code').AsString, ANode.Rec.ValueByName('B_Code').AsString, ANode.Rec.ValueByName('Name').AsString, ANode.Rec.ValueByName('Unit').AsString]); end; procedure ExportNode(ANode: TsdIDTreeNode; AOrder: Integer; AParentPath: string); var sNodePath: string; begin if not Assigned(ANode) then Exit; if AParentPath = '' then sNodePath := IntToStr(ANode.ID) else sNodePath := AParentPath + '.' + IntToStr(ANode.ID); sgs.Strings[sgs.Count - 1] := sgs.Strings[sgs.Count - 1] + '{'; sgs.Add(Format(' %s', [AnsiToUtf8(GetNodeData(ANode, AOrder, sNodePath))])); sgs.Add('}'); if Assigned(ANode.NextNode) then sgs.Strings[sgs.Count - 1] := sgs.Strings[sgs.Count - 1] + ','; ExportNode(ANode.FirstChild, 1, sNodePath); ExportNode(ANode.NextSibling, AOrder + 1, AParentPath); end; begin sgs := TStringList.Create; try sgs.Add('['); ExportNode(FBillsTree.FirstNode, 1, ''); sgs.Strings[sgs.Count - 1] := sgs.Strings[sgs.Count - 1] + ']'; sgs.SaveToFile(AFileName); finally sgs.Free; end; end; end.