Ver código fonte

导入Excel,全部替换控件,以识别“×”

MaiXinRong 9 anos atrás
pai
commit
d420a400f1

+ 5 - 7
Forms/MainFrm.pas

@@ -9,7 +9,6 @@ uses
   AuthFrm,
   // Model & Data & Data Control ...
   ProjectData, SupportUnit, Globals, ZhAPI, ExcelImport, ConditionalDefines,
-  DealBillsExcelImport,
   // Controls & Delphi Default ... (Almost By Add Controls)
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, cxGraphics, JimPages, ComCtrls, dxStatusBar, cxControls, JimTabs,
@@ -234,7 +233,8 @@ implementation
 uses
   ProjectProperty, ConstUnit, PHPWebDm, Math, ShellAPI,
   FindUserFrm, ImportExcelHintFrm, ConfigDoc, ExportExcel,
-  ProjectCommands, BillsCompileDm, tpMainFrm;
+  ProjectCommands, BillsCompileDm, tpMainFrm,
+  DealBillsExcelImport, ExcelImport_Bills, DetailExcelImport;
 
 {$R *.dfm}
 {$R MeasureIcons.RES}
@@ -538,18 +538,16 @@ end;
 procedure TMainForm.actnImportExcelExecute(Sender: TObject);
 var
   sFileName: string;
-  Importor: TBillsEdtExcelImport;
+  Importor: Tdei_CustomBills;
   bWithLevelCode, bWithoutGclBills: Boolean;
 begin
   if HintAndImportTypeSelect(bWithLevelCode, bWithoutGclBills) then
   begin
     if SelectFile(sFileName, '.xls') then
     begin
-      Importor := TBillsEdtExcelImport.Create(CurProjectFrame.ProjectData);
+      Importor := Tdei_CustomBills.Create(CurProjectFrame.ProjectData);
       try
-        Importor.WithLevelCode := bWithLevelCode;
-        Importor.WithoutGclBills := bWithoutGclBills;
-        Importor.ImportFile(sFileName);
+        Importor.ImportFile(sFileName, bWithLevelCode, bWithoutGclBills);
       finally
         Importor.Free;
       end;

+ 4 - 5
Frames/BillsCompileFme.pas

@@ -114,7 +114,7 @@ implementation
 
 uses
   MainFrm, BatchInsertBillsFrm, ExportExcel, ProjectData, mEncryptEditions,
-  ExcelImport, DetailExcelImport, mDataRecord;
+  ExcelImport, DetailExcelImport, mDataRecord, ExcelImport_GclBills;
 
 {$R *.dfm}
 
@@ -524,16 +524,15 @@ procedure TBillsCompileFrame.actnImportGclBillsToXmjExecute(
   Sender: TObject);
 var
   sFileName: string;
-  Importor: TGclBillsExcelImport;
+  Importor: TDEI_GclBills;
 begin
   if TProjectData(FBillsCompileData.ProjectData).CanInsertNormalBills then
   begin
     if SelectFile(sFileName, '.xls') then
     begin
-      Importor := TGclBillsExcelImport.Create(TProjectData(FBillsCompileData.ProjectData));
+      Importor := TDEI_GclBills.Create(TProjectData(FBillsCompileData.ProjectData));
       try
-        Importor.ParentID := stdBillsCompile.IDTree.Selected.ID;
-        Importor.ImportFile(sFileName);
+        Importor.ImportToXmj(sFileName, stdBillsCompile.IDTree.Selected.ID);
       finally
         Importor.Free;
       end;

+ 1 - 0
Units/DealBillsExcelImport.pas

@@ -1,4 +1,5 @@
 unit DealBillsExcelImport;
+// µ¼ÈëǩԼÇåµ¥
 
 interface
 

+ 125 - 1
Units/DetailExcelImport.pas

@@ -80,11 +80,29 @@ type
     property ParentID: Integer read FParentID write FParentID;
   end;
 
+  // 헌데데송
+  TBillsPriceExcelImport = class(TDetailExcelImport)
+  private
+    FCurRow: Integer;
+
+    FB_CodeCol: Integer;
+    FNameCol: Integer;
+    FPriceCol: Integer;
+
+    procedure BeginImport; override;
+    procedure EndImport; override;
+
+    procedure LoadColumnsFromHead;
+    procedure UpdateBillsPrice(const AB_Code: string; APrice: Double);
+    procedure ImportBillsPriceData;
+    procedure Import; override;
+  end;
+
 implementation
 
 uses
   UtilMethods, SysUtils, ZhAPI, SheetSelectFrm, UExcelAdapter, UFlxMessages,
-  UFlxFormats, ProgressHintFrm;
+  UFlxFormats, ProgressHintFrm, mDataRecord;
 
 { TDetailExcelImport }
 
@@ -644,4 +662,110 @@ begin
   Screen.Cursor := crDefault;
 end;
 
+{ TBillsPriceExcelImport }
+
+procedure TBillsPriceExcelImport.BeginImport;
+begin
+  ShowProgressHint('돔흙Excel헌데데송', 100);
+  FProjectData.BillsData.sddBills.BeginUpdate;
+  FCurRow := 1;
+end;
+
+procedure TBillsPriceExcelImport.EndImport;
+begin
+  FProjectData.BillsData.sddBills.EndUpdate;
+  UpdateProgressHint('攣瞳셕炬돔흙빈돨鑒앴');
+  FProjectData.BillsCompileData.CalculateAll;
+  CloseProgressHint;
+end;
+
+procedure TBillsPriceExcelImport.Import;
+begin
+  LoadColumnsFromHead;
+  ImportBillsPriceData;
+end;
+
+procedure TBillsPriceExcelImport.ImportBillsPriceData;
+
+  function CheckIsBillsCode(ACode: string): Boolean;
+  const
+    FBillsCodeSet: set of char = ['0'..'9', '-', 'a'..'z', 'A'..'Z'];
+  var
+    I: Integer;
+  begin
+    Result := True;
+    I := 1;
+    while I < Length(ACode) do
+      if ACode[I] in FBillsCodeSet then
+        Inc(I)
+      else
+      begin
+        Result := False;
+        Break;
+      end;
+  end;
+
+var
+  iPos: Integer;
+  sB_Code: string;
+  fPrice: Double;
+begin
+  UpdateProgressHint('畇흙뗍혤돨Excel鑒앴');
+  UpdateProgressPosition(0);
+  while (FCurRow <= Excel.XlsFile.MaxRow) do
+  begin
+    sB_Code := GetCellTrimStr(Excel.XlsFile, FCurRow, FB_CodeCol);
+    if (sB_Code <> '') and CheckIsBillsCode(sB_Code) then
+    begin
+      fPrice := GetCellFloat(Excel.XlsFile, FCurRow, FPriceCol);
+      UpdateBillsPrice(sB_Code, fPrice);
+    end;
+    Inc(FCurRow);
+    iPos := FCurRow * 100 div Excel.XlsFile.MaxRow;
+    UpdateProgressPosition(iPos);
+  end;
+  UpdateProgressPosition(100);
+end;
+
+procedure TBillsPriceExcelImport.LoadColumnsFromHead;
+var
+  iCol: Integer;
+  sColName: string;
+begin
+  FB_CodeCol := -1;
+  FNameCol := -1;
+  FPriceCol := -1;
+  while ((FB_CodeCol = -1) or (FPriceCol = -1)) and (FCurRow <= Excel.XlsFile.MaxRow) do
+  begin
+    for iCol := 1 to Excel.XlsFile.MaxCol do
+    begin
+      sColName := GetCellTrimStr(Excel.XlsFile, FCurRow, iCol);
+      if SameText(sColName, '헌데긍뵀') or SameText(sColName, '綾커뵀') then
+        FB_CodeCol := iCol
+      else if SameText(sColName, '츰냔') then
+        FNameCol := iCol
+      else if Pos('데송', sColName) = 1 then
+        FPriceCol := iCol;
+    end;
+    Inc(FCurRow);
+  end;
+end;
+
+procedure TBillsPriceExcelImport.UpdateBillsPrice(const AB_Code: string;
+  APrice: Double);
+var
+  iIndex: Integer;
+  Rec: TBillsRecord;
+begin
+  with FProjectData.BillsData do
+  begin
+    for iIndex := 0 to sddBills.RecordCount - 1 do
+    begin
+      Rec := TBillsRecord(sddBills.Records[iIndex]);
+      if SameText(AB_Code, Rec.B_Code.AsString) then
+        Rec.Price.AsFloat := PriceRoundTo(APrice);
+    end;
+  end;
+end;
+
 end.

+ 0 - 405
Units/ExcelImport.pas

@@ -104,72 +104,6 @@ type
     property WithoutGclBills: Boolean read FWithoutGclBills write FWithoutGclBills;
   end;
 
-  TGclBillsExcelImport = class(TExcelImport)
-  private
-    FParentID: Integer;
-    FSelectSheets: TList;
-    FCacheTree: TGclCacheTree;
-    FCurRow: Integer;
-
-    FB_CodeCol: Integer;
-    FNameCol: Integer;
-    FUnitsCol: Integer;
-    FPriceCol: Integer;
-    FQuantityCol: Integer;
-
-    procedure BeginImport; override;
-    procedure EndImport; override;
-
-    procedure LoadNode(ASheet: TSpreadSheet);
-    procedure ImportSheet(ASheet: TSpreadSheet);
-
-    procedure WriteNode(ADataSet: TsdDataSet; ANode: TGclCacheNode);
-    procedure WriteNodes(ADataSet: TsdDataSet);
-
-    procedure Import; override;
-  public
-    property ParentID: Integer read FParentID write FParentID;
-  end;
-
-  // 清单单价
-  TBillsPriceExcelImport = class(TExcelImport)
-  private
-    FCurRow: Integer;
-
-    FB_CodeCol: Integer;
-    FNameCol: Integer;
-    FPriceCol: Integer;
-
-    procedure BeginImport; override;
-    procedure EndImport; override;
-
-    procedure LoadColumnsFromHead(ASheet: TSpreadSheet);
-    procedure UpdateBillsPrice(const AB_Code: string; APrice: Double);
-    procedure ImportBillsPriceData(ASheet: TSpreadSheet);
-    procedure Import; override;
-  end;
-
-  // 合同清单
-  {TDealBillsExcelImport = class(TExcelImport)
-  private
-    FCurRow: Integer;
-    FBillsID: Integer;
-
-    FB_CodeCol: Integer;
-    FNameCol: Integer;
-    FUnitsCol: Integer;
-    FPriceCol: Integer;
-    FQuantityCol: Integer;
-    FTotalPriceCol: Integer;
-
-    procedure BeginImport; override;
-    procedure EndImport; override;
-
-    procedure LoadColumnsFromHead(ASheet: TSpreadSheet);
-    procedure LoadDealBillsData(ASheet: TSpreadSheet);
-    procedure Import; override;
-  end;}
-
 implementation
 
 uses Variants, CacheTree, SysUtils, UtilMethods, sdDataSet, BillsDm,
@@ -662,343 +596,4 @@ begin
   end;
 end;
 
-{ TBillsPriceExcelImport }
-
-procedure TBillsPriceExcelImport.BeginImport;
-begin
-  ShowProgressHint('导入Excel清单单价', 100);
-  FProjectData.BillsData.sddBills.BeginUpdate;
-end;
-
-procedure TBillsPriceExcelImport.EndImport;
-begin
-  FProjectData.BillsData.sddBills.EndUpdate;
-  UpdateProgressHint('正在计算导入后的数据');
-  FProjectData.BillsCompileData.CalculateAll;
-  CloseProgressHint;
-end;
-
-procedure TBillsPriceExcelImport.Import;
-begin
-  LoadColumnsFromHead(FMSExcel.Sheets.Spreadsheet(0));
-  ImportBillsPriceData(FMSExcel.Sheets.Spreadsheet(0));
-end;
-
-procedure TBillsPriceExcelImport.ImportBillsPriceData(ASheet: TSpreadSheet);
-
-  function CheckIsBillsCode(ACode: string): Boolean;
-  const
-    FBillsCodeSet: set of char = ['0'..'9', '-', 'a'..'z', 'A'..'Z'];
-  var
-    I: Integer;
-  begin
-    Result := True;
-    I := 1;
-    while I < Length(ACode) do
-      if ACode[I] in FBillsCodeSet then
-        Inc(I)
-      else
-      begin
-        Result := False;
-        Break;
-      end;
-  end;
-
-var
-  iPos: Integer;
-  sB_Code: string;
-  fPrice: Double;
-begin
-  UpdateProgressHint('写入读取的Excel数据');
-  UpdateProgressPosition(0);
-  while (FCurRow < ASheet.Cells.UsedRowCount) do
-  begin
-    sB_Code := GetCellTrimText(ASheet, FB_CodeCol, FCurRow);
-    if (sB_Code <> '') and CheckIsBillsCode(sB_Code) then
-    begin
-      fPrice := StrToFloatDef(VarToStrDef(ASheet.Cells.GetValue(FPriceCol, FCurRow), ''), 0);
-      UpdateBillsPrice(sB_Code, fPrice);
-    end;
-    Inc(FCurRow);
-    iPos := FCurRow * 100 div ASheet.Cells.UsedRowCount;
-    UpdateProgressPosition(iPos);
-  end;
-  UpdateProgressPosition(100);
-end;
-
-procedure TBillsPriceExcelImport.LoadColumnsFromHead(ASheet: TSpreadSheet);
-var
-  iCol: Integer;
-  sColName: string;
-begin
-  FB_CodeCol := -1;
-  FNameCol := -1;
-  FPriceCol := -1;
-  while ((FB_CodeCol = -1) or (FPriceCol = -1)) and (FCurRow < ASheet.Cells.UsedRowCount) do
-  begin
-    for iCol := 0 to ASheet.Cells.UsedColCount do
-    begin
-      sColName := GetCellTrimText(ASheet, iCol, FCurRow);
-      if SameText(sColName, '清单编号') or SameText(sColName, '子目号') then
-        FB_CodeCol := iCol
-      else if SameText(sColName, '名称') then
-        FNameCol := iCol
-      else if Pos('单价', sColName) = 1 then
-        FPriceCol := iCol;
-    end;
-    Inc(FCurRow);
-  end;
-end;
-
-procedure TBillsPriceExcelImport.UpdateBillsPrice(const AB_Code: string;
-  APrice: Double);
-var
-  iIndex: Integer;
-  Rec: TBillsRecord;
-begin
-  with FProjectData.BillsData do
-  begin
-    for iIndex := 0 to sddBills.RecordCount - 1 do
-    begin
-      Rec := TBillsRecord(sddBills.Records[iIndex]);
-      if SameText(AB_Code, Rec.B_Code.AsString) then
-        Rec.Price.AsFloat := PriceRoundTo(APrice);
-    end;
-  end;
-end;
-
-{ TDealBillsExcelImport }
-
-{procedure TDealBillsExcelImport.BeginImport;
-begin
-  FProjectData.DealBillsData.sddDealBills.BeginUpdate;
-end;
-
-procedure TDealBillsExcelImport.EndImport;
-begin
-  FProjectData.DealBillsData.sddDealBills.EndUpdate;
-end;
-
-procedure TDealBillsExcelImport.Import;
-begin
-  LoadColumnsFromHead(FMSExcel.Sheets.Spreadsheet(0));
-  FBillsID := 1;
-  FProjectData.DealBillsData.Clear;
-  FProjectData.DealBillsData.DisableEvent;
-  LoadDealBillsData(FMSExcel.Sheets.Spreadsheet(0));
-  FProjectData.DealBillsData.EnableEvent;
-end;
-
-procedure TDealBillsExcelImport.LoadColumnsFromHead(ASheet: TSpreadSheet);
-var
-  iCol: Integer;
-  sColName: string;
-begin
-  FB_CodeCol := -1;
-  FNameCol := 1;
-  FUnitsCol := 2;
-  FPriceCol := 3;
-  FQuantityCol := 4;
-  FTotalPriceCol := 5;
-  while ((FB_CodeCol = -1) or (FPriceCol = -1)) and (FCurRow < ASheet.Cells.UsedRowCount) do
-  begin
-    for iCol := 0 to ASheet.Cells.UsedColCount do
-    begin
-      sColName := GetCellTrimText(ASheet, iCol, FCurRow);
-      if SameText(sColName, '清单编号') or SameText(sColName, '子目号') then
-        FB_CodeCol := iCol
-      else if SameText(sColName, '名称') then
-        FNameCol := iCol
-      else if SameText(sColName, '单位') then
-        FUnitsCol := iCol
-      else if Pos('单价', sColName) = 1 then
-        FPriceCol := iCol
-      else if SameText(sColName, '数量') then
-        FQuantityCol := iCol
-      else if SameText(sColName, '金额') then
-        FTotalPriceCol := iCol;
-    end;
-    Inc(FCurRow);
-  end;
-end;
-
-procedure TDealBillsExcelImport.LoadDealBillsData(ASheet: TSpreadSheet);
-
-  function CheckIsBillsCode(ACode: string): Boolean;
-  const
-    FBillsCodeSet: set of char = ['0'..'9', '-', 'a'..'z', 'A'..'Z'];
-  var
-    I: Integer;
-  begin
-    Result := True;
-    I := 1;
-    while I < Length(ACode) do
-      if ACode[I] in FBillsCodeSet then
-        Inc(I)
-      else
-      begin
-        Result := False;
-        Break;
-      end;
-  end;
-
-var
-  sB_Code: string;
-  Rec: TsdDataRecord;
-begin
-  while (FCurRow < ASheet.Cells.UsedRowCount) do
-  begin
-    sB_Code := GetCellTrimText(ASheet, FB_CodeCol, FCurRow);
-    if (sB_Code <> '') and CheckIsBillsCode(sB_Code) then
-    begin
-      Rec := FProjectData.DealBillsData.sddDealBills.Add;
-      Rec.ValueByName('ID').AsInteger := FBillsID;
-      Rec.ValueByName('B_Code').AsString := sB_Code; 
-      Rec.ValueByName('IndexCode').AsString := B_CodeToIndexCode(sB_Code);
-      Rec.ValueByName('Name').AsString := GetCellTrimText(ASheet, FNameCol, FCurRow);
-      Rec.ValueByName('Units').AsString := GetCellTrimText(ASheet, FUnitsCol, FCurRow);
-      Rec.ValueByName('Price').AsFloat := PriceRoundTo(
-          StrToFloatDef(GetCellTrimText(ASheet, FPriceCol, FCurRow), 0));
-      Rec.ValueByName('Quantity').AsFloat := QuantityRoundTo(
-          StrToFloatDef(GetCellTrimText(ASheet, FQuantityCol, FCurRow), 0));
-      Rec.ValueByName('TotalPrice').AsFloat := TotalPriceRoundTo(
-          StrToFloatDef(GetCellTrimText(ASheet, FTotalPriceCol, FCurRow), 0));
-      Inc(FBillsID);
-    end;
-    Inc(FCurRow);
-  end;
-end;}
-
-{ TGclBillsExcelImport }
-
-procedure TGclBillsExcelImport.BeginImport;
-begin
-  Screen.Cursor := crHourGlass;
-  ShowProgressHint('导入Excel数据', 100);
-
-  FCacheTree := TGclCacheTree.Create;
-  FCacheTree.NewNodeID := FProjectData.BillsData.GetMaxBillsID + 1;
-
-  FProjectData.DisConnectTree;
-  FProjectData.BillsData.DisableEvents;
-
-  FSelectSheets := TList.Create;
-
-  FB_CodeCol := 0;
-  FNameCol := 1;
-  FUnitsCol := 2;
-  FQuantityCol := 3;
-  FPriceCol := 4;
-end;
-
-procedure TGclBillsExcelImport.EndImport;
-var
-  ParentRec: TsdDataRecord;
-begin
-  FSelectSheets.Free;
-
-  FCacheTree.Free;
-
-  FProjectData.BillsData.EnableEvents;
-  FProjectData.ReConnectTree;
-
-  ParentRec := FProjectData.BillsData.sddBills.FindKey('idxID', ParentID);
-  FProjectData.BillsCompileData.sdvBillsCompile.LocateInControl(ParentRec);
-
-  FProjectData.BillsCompileData.CalculateAll;
-
-  CloseProgressHint;
-  Screen.Cursor := crDefault;
-end;
-
-procedure TGclBillsExcelImport.Import;
-var
-  i: Integer;
-begin
-  {if SelectSheets(FMSExcel, FSelectSheets) then
-  begin
-    for i := 0 to FSelectSheets.Count - 1 do
-    begin
-      UpdateProgressHint(Format('导入Excel数据--工作表[%s]', [FMSExcel.SheetNames.Strings[i]]));
-      UpdateProgressPosition(0);
-      ImportSheet(FSelectSheets.Items[i]);
-    end;
-  end;}
-  ImportSheet(FMSExcel.Sheets.Spreadsheet(0));
-  WriteNodes(FProjectData.BillsData.sddBills);
-end;
-
-procedure TGclBillsExcelImport.ImportSheet(ASheet: TSpreadSheet);
-var
-  iPos: Integer;
-begin
-  FCurRow := 1;
-  while (FCurRow < ASheet.Cells.UsedRowCount) do
-  begin
-    LoadNode(ASheet);
-    Inc(FCurRow);
-    iPos := FCurRow * 100 div ASheet.Cells.UsedRowCount;
-    UpdateProgressPosition(iPos);
-  end;
-end;
-
-procedure TGclBillsExcelImport.LoadNode(ASheet: TSpreadSheet);
-var
-  sB_Code, sName: string;
-  Node: TGclCacheNode;
-begin
-  with ASheet.Cells do
-  begin
-    sB_Code := Trim(VarToStrDef(GetValue(FB_CodeCol, FCurRow), ''));
-    sName := Trim(VarToStrDef(GetValue(FNameCol, FCurRow), ''));
-
-    Node := FCacheTree.AddNodeByData(sB_Code, sName);
-
-    Node.B_Code := sB_Code;
-    Node.Name := sName;
-    Node.Units := Trim(VarToStrDef(GetValue(FUnitsCol, FCurRow), ''));
-    Node.Price := StrToFloatDef(VarToStrDef(GetValue(FPriceCol, FCurRow), ''), 0);
-    Node.Quantity := StrToFloatDef(VarToStrDef(GetValue(FQuantityCol, FCurRow), ''), 0);
-  end;
-end;
-
-procedure TGclBillsExcelImport.WriteNodes(ADataSet: TsdDataSet);
-var
-  i, iPos: Integer;
-begin
-  UpdateProgressHint('写入读取的Excel数据');
-  UpdateProgressPosition(0);
-  for i := 0 to FCacheTree.CacheNodes.Count - 1 do
-  begin
-    WriteNode(ADataSet, TGclCacheNode(FCacheTree.CacheNodes[i]));
-    iPos := i*100 div FCacheTree.CacheNodes.Count;
-    UpdateProgressPosition(iPos);
-  end;
-  UpdateProgressPosition(100);
-end;
-
-procedure TGclBillsExcelImport.WriteNode(ADataSet: TsdDataSet;
-  ANode: TGclCacheNode);
-var
-  Rec: TBillsRecord;
-begin
-  if ANode.B_Code <> '' then
-    UpdateProgressHint('写入读取的Excel数据 ' + ANode.B_Code)
-  else
-    UpdateProgressHint('写入读取的Excel数据 ' + ANode.Name);
-
-  Rec := TBillsRecord(ADataSet.Add);
-  Rec.ID.AsInteger := ANode.ID;
-  if ANode.ParentID = -1 then
-    Rec.ParentID.AsInteger := ParentID
-  else
-    Rec.ParentID.AsInteger := ANode.ParentID;
-  Rec.NextSiblingID.AsInteger := ANode.NextSiblingID;
-  Rec.B_Code.AsString := ANode.B_Code;
-  Rec.Name.AsString := ANode.Name;
-  Rec.Units.AsString := ANode.Units;
-  Rec.Price.AsFloat := PriceRoundTo(ANode.Price);
-  Rec.OrgQuantity.AsFloat := QuantityRoundTo(ANode.Quantity);
-end;
-
 end.

+ 403 - 0
Units/ExcelImport_Bills.pas

@@ -0,0 +1,403 @@
+unit ExcelImport_Bills;
+
+// 导入0号台账/分项清单
+
+interface
+
+uses
+  DetailExcelImport, Classes, MCacheTree, sdDB, ProjectData;
+
+type
+  Tdei_CustomBills = class(TDetailExcelImport)
+  private
+    FCacheTree: TBillsCacheTree;
+    FCurRow: Integer;
+    FIsFirstPart: Boolean;
+
+    FWithLevelCode: Boolean;
+    FWithoutGclBills: Boolean;
+
+    FBaseTree: TBillsCacheTree;
+    FFixedIDNodes: TList;
+
+    FCodeCol: Integer;
+    FB_CodeCol: Integer;
+    FNameCol: Integer;
+    FUnitsCol: Integer;
+    FPriceCol: Integer;
+    FQuantityCol: Integer;
+    FDgnQuantity1Col: Integer;
+    FDgnQuantity2Col: Integer;
+    FDrawingCol: Integer;
+    FMemoCol: Integer;
+    FLevelCol: Integer;
+  protected
+    procedure BeginImport; override;
+    procedure EndImport; override;
+
+    function GetFixedIDNode(AID: Integer): TBillsCacheNode;
+    function GetFixedID(ACode, AName: string): Integer;
+
+    procedure CheckFixedIDNodes;
+
+    procedure LoadColumnsFromHead;
+
+    procedure LoadBaseTree(ATree: TBillsCacheTree);
+
+    procedure LoadNode;
+    procedure LoadNodes;
+
+    procedure WriteNode(ADataSet: TsdDataSet; ANode: TBillsCacheNode);
+    procedure WriteNodes(ADataSet: TsdDataSet);
+
+    procedure Import; override;
+  public
+    procedure ImportFile(const AFileName: string; AWithLevelCode, AWithoutGclBills: Boolean); overload;
+  end;
+
+implementation
+
+uses
+  Forms, Controls, ProgressHintFrm, ConstUnit, UtilMethods, ExcelImport,
+  SysUtils, mDataRecord;
+
+{ Tdei_CustomBills }
+
+procedure Tdei_CustomBills.BeginImport;
+begin
+  Screen.Cursor := crHourGlass;
+  ShowProgressHint('导入Excel数据', 100, '读取Excel数据', 100);
+
+  FCurRow := 1;
+  FIsFirstPart := True;
+  FCacheTree := TBillsCacheTree.Create;
+  FCacheTree.NewNodeID := 101;
+  // 以层次编号为依据,分隔用'.',以项目节、清单编号为依据,分隔用'-'
+  if FWithLevelCode then
+    FCacheTree.SeparateChar := '.'
+  else
+    FCacheTree.SeparateChar := '-';
+  FCacheTree.AutoSort := True;
+
+  ProjectData.DisConnectTree;
+  ProjectData.BillsData.DisableEvents;
+
+  FBaseTree := TBillsCacheTree.Create;
+  FBaseTree.NewNodeID := 101;
+  FBaseTree.SeparateChar := '.';
+  FFixedIDNodes := TList.Create;
+end;
+
+procedure Tdei_CustomBills.CheckFixedIDNodes;
+
+  function GetHintStr(ANode: TBillsCacheNode): string;
+  begin
+    Result := '';
+    if ANode.Code <> '' then
+      Result := Result + '编号:' + ANode.Code + ';';
+    if ANode.Name <> '' then
+      Result := Result + '名称:' + ANode.Name + ';';
+  end;
+
+  function GetInvalidModel(ANoPM: Boolean; ACount: Integer): string;
+  begin
+    if ANoPM then
+    begin
+      if ACount > 1 then
+        Result := '价差功能,部分报表'
+      else
+        Result := '价差功能'
+    end
+    else
+      Result := '部分报表';
+    Result := Result + '将不可使用' + #13#10 + '如有疑问,请联系纵横客服,企业QQ:800003850 电话:(0756)3850888';
+  end;
+
+var
+  sgs: TStrings;
+  iBase: Integer;
+  vBase, vImport: TBillsCacheNode;
+  sHint: string;
+  bNoPM: Boolean;
+begin
+  bNoPM := False;
+  sgs := TStringList.Create;
+  try
+    sgs.Add('缺少如下固定清单节点:');
+    for iBase := 0 to FBaseTree.FixedIDNodes.Count - 1 do
+    begin
+      vBase := TBillsCacheNode(FBaseTree.FixedIDNodes.Items[iBase]);
+
+      vImport := FCacheTree.FindFixedIDNode(vBase.ID);
+      if not Assigned(vImport) then
+      begin
+        sgs.Add(GetHintStr(vBase));
+        if vBase.ID = iPriceMarginID then
+          bNoPM := True;
+      end;
+    end;
+  finally
+    if sgs.Count > 1 then
+    begin
+      sgs.Add(GetInvalidModel(bNoPM, sgs.Count - 1));
+      WarningMessage(sgs.Text);
+    end;
+    sgs.Free;
+  end;
+end;
+
+procedure Tdei_CustomBills.EndImport;
+begin
+  // For Test
+  // FCacheTree.SaveTreeToFile('E:\Tree.txt');
+
+  if FWithLevelCode then
+    CheckFixedIDNodes;
+
+  FFixedIDNodes.Free;
+  FBaseTree.Free;
+  ProjectData.BillsData.EnableEvents;
+  ProjectData.ReConnectTree;
+  FCacheTree.Free;
+  ProjectData.BillsCompileData.CalculateAll;
+  CloseProgressHint;
+  Screen.Cursor := crDefault;
+end;
+
+function Tdei_CustomBills.GetFixedID(ACode, AName: string): Integer;
+var
+  i: Integer;
+  Node: TBillsCacheNode;
+begin
+  Result := -1;
+  for i := 0 to FBaseTree.CacheNodes.Count - 1 do
+  begin
+    Node := TBillsCacheNode(FBaseTree.CacheNodes.Items[i]);
+    if (Node.Code = ACode) and (Node.Name = AName) then
+    begin
+      if Node.ID < 100 then
+        Result := Node.ID;
+      Break;
+    end;
+  end;
+end;
+
+function Tdei_CustomBills.GetFixedIDNode(AID: Integer): TBillsCacheNode;
+var
+  i: Integer;
+  Node: TBillsCacheNode;
+begin
+  Result := nil;
+  for i := 0 to FFixedIDNodes.Count - 1 do
+  begin
+    Node := TBillsCacheNode(FFixedIDNodes.Items[i]);
+    if (AID = Node.ID) then
+    begin
+      Result := Node;
+      Break;
+    end;
+  end;
+end;
+
+procedure Tdei_CustomBills.Import;
+begin
+  if FWithLevelCode then
+    LoadBaseTree(FBaseTree)
+  else
+    LoadBaseTree(FCacheTree);
+  LoadColumnsFromHead;
+  LoadNodes;
+  WriteNodes(ProjectData.BillsData.sddBills);
+end;
+
+procedure Tdei_CustomBills.ImportFile(const AFileName: string;
+  AWithLevelCode, AWithoutGclBills: Boolean);
+begin
+  FWithLevelCode := AWithLevelCode;
+  FWithoutGclBills := AWithoutGclBills;
+  ImportFile(AFileName);
+end;
+
+procedure Tdei_CustomBills.LoadBaseTree(ATree: TBillsCacheTree);
+var
+  BaseImportor: TBillsExcelImport;
+begin
+  BaseImportor := TBillsExcelImport.Create(nil);
+  try
+    BaseImportor.ImportToTree(ATree, GetTemplateBillsFileName);
+  finally
+    BaseImportor.Free;
+  end;
+end;
+
+procedure Tdei_CustomBills.LoadColumnsFromHead;
+var
+  iCol: Integer;
+  sColName: string;
+begin
+  FCodeCol := -1;
+  FB_CodeCol := -1;
+  FNameCol := -1;
+  FUnitsCol := -1;
+  FPriceCol := -1;
+  FQuantityCol := -1;
+  FDgnQuantity1Col := -1;
+  FDgnQuantity2Col := -1;
+  FDrawingCol := -1;
+  FMemoCol := -1;
+  for iCol := 1 to Excel.XlsFile.MaxRow do
+  begin
+    sColName := GetCellTrimStr(Excel.XlsFile, FCurRow, iCol);
+
+    if (sColName = '预算项目节') or (sColName = '项目节编号') then
+      FCodeCol := iCol
+    else if (sColName = '清单子目号') or (sColName = '清单编号') then
+      FB_CodeCol := iCol
+    else if sColName = '名称' then
+      FNameCol := iCol
+    else if sColName = '单位' then
+      FUnitsCol := iCol
+    else if sColName = '单价' then
+      FPriceCol := iCol
+    else if (sColName = '清单数量') or (sColName = '工程量') or (sColName = '数量') then
+      FQuantityCol := iCol
+    else if (sColName = '设计数量1') or (sColName = '数量1') then
+      FDgnQuantity1Col := iCol
+    else if (sColName = '设计数量2') or (sColName = '数量2') then
+      FDgnQuantity2Col := iCol
+    else if sColName = '图号' then
+      FDrawingCol := iCol
+    else if sColName = '备注' then
+      FMemoCol := iCol
+    else if sColName = '层次编号' then
+      FLevelCol := iCol;
+  end;
+  Inc(FCurRow);
+end;
+
+procedure Tdei_CustomBills.LoadNode;
+var
+  sLevelCode, sCode, sB_Code, sName: string;
+  Node: TBillsCacheNode;
+  vValue: Variant;
+  iFixedID: Integer;
+begin
+  sLevelCode := GetCellTrimStr(Excel.XlsFile, FCurRow, FLevelCol);
+  sCode := GetCellTrimStr(Excel.XlsFile, FCurRow, FCodeCol);
+  sB_Code := GetCellTrimStr(Excel.XlsFile, FCurRow, FB_CodeCol);
+  sName := GetCellTrimStr(Excel.XlsFile, FCurRow, FNameCol);
+
+  // 含层次编号时,层次编号为空不导入
+  // 不含层次编号时,仅导入第一部分,且项目节编号、清单编号均未空时不导入
+  if FWithLevelCode then
+  begin
+    if sLevelCode = '' then Exit;
+  end
+  else
+  begin
+    if ((sCode = '') and (sB_Code = '')) or SameText(sCode, '2') or
+        (Pos('第二部分', sName) > 0) then
+    begin
+      FIsFirstPart := False;
+      Exit;
+    end;
+  end;
+
+  if (sCode = '') and FWithoutGclBills then Exit;
+
+  // 含层次编号时,以层次编号为依据新增节点;反之以项目节编号为依据新增节点
+  if not FWithLevelCode then
+  begin
+    if (sCode <> '') then
+      Node := FCacheTree.AddNodeByCode(sCode, -1)
+    else
+      Node := FCacheTree.AddLeafBillsNode(sB_Code);
+  end
+  else
+  begin
+    // 1. 从模板树中查询当前节点是否为固定ID,否则为-1
+    iFixedID := GetFixedID(sCode, sName);
+    // 2. 从导入树中查询是否添加过该固定ID,防止存在两个固定ID节点主键冲突
+    //    如果已添加过固定ID节点,则其他节点未非固定ID节点
+    Node := GetFixedIDNode(iFixedID);
+    if Assigned(Node) then
+      iFixedID := -1;
+    // 3. 添加当前节点
+    Node := FCacheTree.AddNodeByCode(sLevelCode, iFixedID);
+    // 4. 如果当前添加的节点为固定ID节点,则添加到List中便于2快速查找
+    if Node.ID < 100 then
+      FFixedIDNodes.Add(Node);
+  end;
+
+  Node.Code := sCode;
+  Node.B_Code := sB_Code;
+  Node.Name := sName;
+  Node.Units := GetCellTrimStr(Excel.XlsFile, FCurRow, FUnitsCol);
+  Node.Price := GetCellFloat(Excel.XlsFile, FCurRow, FPriceCol);
+  Node.Quantity := GetCellFloat(Excel.XlsFile, FCurRow, FQuantityCol);
+  Node.DgnQuantity1 := GetCellFloat(Excel.XlsFile, FCurRow, FDgnQuantity1Col);
+  Node.DgnQuantity2 := GetCellFloat(Excel.XlsFile, FCurRow, FDgnQuantity2Col);
+  Node.DrawingCode := GetCellTrimStr(Excel.XlsFile, FCurRow, FDrawingCol);
+  Node.MemoStr := GetCellTrimStr(Excel.XlsFile, FCurRow, FMemoCol);
+end;
+
+procedure Tdei_CustomBills.LoadNodes;
+var
+  iPos, iSubPos: Integer;
+begin
+  while (FCurRow <= Excel.XlsFile.MaxRow){ and FIsFirstPart }do
+  begin
+    LoadNode;
+    Inc(FCurRow);
+    iSubPos := FCurRow * 100 div Excel.XlsFile.MaxRow;
+    iPos := iSubPos div 2;
+    UpdateProgressPosition(iPos, iSubPos);
+  end;
+end;
+
+procedure Tdei_CustomBills.WriteNode(ADataSet: TsdDataSet;
+  ANode: TBillsCacheNode);
+var
+  Rec: TBillsRecord;
+begin
+  if ANode.Code <> '' then
+    UpdateProgressHint('写入读取的Excel数据 ' + ANode.Code)
+  else if ANode.B_Code <> '' then
+    UpdateProgressHint('写入读取的Excel数据 ' + ANode.B_Code)
+  else
+    UpdateProgressHint('写入读取的Excel数据 ' + ANode.Name);
+
+  Rec := TBillsRecord(ADataSet.Add);
+  Rec.ID.AsInteger := ANode.ID;
+  Rec.ParentID.AsInteger := ANode.ParentID;
+  Rec.NextSiblingID.AsInteger := ANode.NextSiblingID;
+  Rec.Code.AsString := ANode.Code;
+  Rec.B_Code.AsString := ANode.B_Code;
+  Rec.Name.AsString := ANode.Name;
+  Rec.Units.AsString := ANode.Units;
+  Rec.Price.AsFloat := PriceRoundTo(ANode.Price);
+  Rec.OrgQuantity.AsFloat := QuantityRoundTo(ANode.Quantity);
+  Rec.DgnQuantity1.AsFloat := QuantityRoundTo(ANode.DgnQuantity1);
+  Rec.DgnQuantity2.AsFloat := QuantityRoundTo(ANode.DgnQuantity2);
+  Rec.DrawingCode.AsString := ANode.DrawingCode;
+  Rec.MemoStr.AsString := ANode.MemoStr;
+end;
+
+procedure Tdei_CustomBills.WriteNodes(ADataSet: TsdDataSet);
+var
+  i, iPos, iSubPos: Integer;
+begin
+  UpdateProgressHint('写入读取的Excel数据', True);
+  UpdateProgressPosition(50, 0);
+  ADataSet.DeleteAll;
+  for i := 0 to FCacheTree.CacheNodes.Count - 1 do
+  begin
+    WriteNode(ADataSet, TBillsCacheNode(FCacheTree.CacheNodes[i]));
+    iSubPos := i*100 div FCacheTree.CacheNodes.Count;
+    iPos := 50 + iSubPos div 2;
+    UpdateProgressPosition(iPos, iSubPos);
+  end;
+  UpdateProgressPosition(100, 100);
+end;
+
+end.

+ 174 - 0
Units/ExcelImport_GclBills.pas

@@ -0,0 +1,174 @@
+unit ExcelImport_GclBills;
+// 导入工程量清单至项目节
+
+interface
+
+uses
+  Classes, DetailExcelImport, MCacheTree, sdDB;
+
+type
+  TDEI_GclBills = class(TDetailExcelImport)
+  private
+    FParentID: Integer;
+    FSelectSheets: TList;
+    FCacheTree: TGclCacheTree;
+    FCurRow: Integer;
+
+    FB_CodeCol: Integer;
+    FNameCol: Integer;
+    FUnitsCol: Integer;
+    FPriceCol: Integer;
+    FQuantityCol: Integer;
+  protected
+    procedure BeginImport; override;
+    procedure EndImport; override;
+
+    procedure Import; override;
+
+    procedure ImportSheet;
+
+    procedure WriteNode(ADataSet: TsdDataSet; ANode: TGclCacheNode);
+    procedure WriteNodes(ADataSet: TsdDataSet);
+  public
+    procedure ImportToXmj(const AFileName: string; AParentID: Integer);
+  end;
+
+implementation
+
+uses
+  Forms, mDataRecord, Controls, ProgressHintFrm, UtilMethods;
+
+{ TDEI_GclBills }
+
+procedure TDEI_GclBills.BeginImport;
+begin
+  Screen.Cursor := crHourGlass;
+  ShowProgressHint('导入Excel数据', 100);
+
+  FCacheTree := TGclCacheTree.Create;
+  FCacheTree.NewNodeID := ProjectData.BillsData.GetMaxBillsID + 1;
+
+  ProjectData.DisConnectTree;
+  ProjectData.BillsData.DisableEvents;
+
+  FSelectSheets := TList.Create;
+
+  FB_CodeCol := 1;
+  FNameCol := 2;
+  FUnitsCol := 3;
+  FQuantityCol := 4;
+  FPriceCol :=  5;
+end;
+
+procedure TDEI_GclBills.EndImport;
+var
+  ParentRec: TsdDataRecord;
+begin
+  FSelectSheets.Free;
+
+  FCacheTree.Free;
+
+  ProjectData.BillsData.EnableEvents;
+  ProjectData.ReConnectTree;
+
+  ParentRec := ProjectData.BillsData.sddBills.FindKey('idxID', FParentID);
+  ProjectData.BillsCompileData.sdvBillsCompile.LocateInControl(ParentRec);
+
+  ProjectData.BillsCompileData.CalculateAll;
+
+  CloseProgressHint;
+  Screen.Cursor := crDefault;
+end;
+
+procedure TDEI_GclBills.Import;
+var
+  i: Integer;
+begin
+  {if SelectSheets(FMSExcel, FSelectSheets) then
+  begin
+    for i := 0 to FSelectSheets.Count - 1 do
+    begin
+      UpdateProgressHint(Format('导入Excel数据--工作表[%s]', [FMSExcel.SheetNames.Strings[i]]));
+      UpdateProgressPosition(0);
+      ImportSheet(FSelectSheets.Items[i]);
+    end;
+  end;}
+  ImportSheet;
+  WriteNodes(ProjectData.BillsData.sddBills);
+end;
+
+procedure TDEI_GclBills.ImportSheet;
+var
+  iPos: Integer; 
+  sB_Code, sName: string;
+  Node: TGclCacheNode;
+begin
+  FCurRow := 2;
+  while (FCurRow <= Excel.XlsFile.MaxRow) do
+  begin
+    sB_Code := GetCellTrimStr(Excel.XlsFile, FCurRow, FB_CodeCol);
+    sName := GetCellTrimStr(Excel.XlsFile, FCurRow, FNameCol);
+
+    if (sB_Code <> '') or (sName <> '') then
+    begin
+      Node := FCacheTree.AddNodeByData(sB_Code, sName);
+      Node.B_Code := sB_Code;
+      Node.Name := sName;
+      Node.Units := GetCellTrimStr(Excel.XlsFile, FCurRow, FUnitsCol);
+      Node.Price := GetCellFloat(Excel.XlsFile, FCurRow, FPriceCol);
+      Node.Quantity := GetCellFloat(Excel.XlsFile, FCurRow, FQuantityCol);
+    end;
+
+    Inc(FCurRow);
+    iPos := FCurRow * 100 div Excel.XlsFile.MaxRow;
+    UpdateProgressPosition(iPos);
+  end;
+end;
+
+procedure TDEI_GclBills.ImportToXmj(const AFileName: string;
+  AParentID: Integer);
+begin
+  FParentID := AParentID;
+  ImportFile(AFileName);
+end;
+
+procedure TDEI_GclBills.WriteNode(ADataSet: TsdDataSet;
+  ANode: TGclCacheNode);
+var
+  Rec: TBillsRecord;
+begin
+  if ANode.B_Code <> '' then
+    UpdateProgressHint('写入读取的Excel数据 ' + ANode.B_Code)
+  else
+    UpdateProgressHint('写入读取的Excel数据 ' + ANode.Name);
+
+  Rec := TBillsRecord(ADataSet.Add);
+  Rec.ID.AsInteger := ANode.ID;
+  if ANode.ParentID = -1 then
+    Rec.ParentID.AsInteger := FParentID
+  else
+    Rec.ParentID.AsInteger := ANode.ParentID;
+  Rec.NextSiblingID.AsInteger := ANode.NextSiblingID;
+  Rec.B_Code.AsString := ANode.B_Code;
+  Rec.Name.AsString := ANode.Name;
+  Rec.Units.AsString := ANode.Units;
+  Rec.Price.AsFloat := PriceRoundTo(ANode.Price);
+  Rec.OrgQuantity.AsFloat := QuantityRoundTo(ANode.Quantity);
+end;
+
+procedure TDEI_GclBills.WriteNodes(ADataSet: TsdDataSet);
+var
+  i, iPos: Integer;
+begin
+  UpdateProgressHint('写入读取的Excel数据');
+  UpdateProgressPosition(0);
+  for i := 0 to FCacheTree.CacheNodes.Count - 1 do
+  begin
+    WriteNode(ADataSet, TGclCacheNode(FCacheTree.CacheNodes[i]));
+    iPos := i*100 div FCacheTree.CacheNodes.Count;
+    UpdateProgressPosition(iPos);
+  end;
+  UpdateProgressPosition(100);
+end;
+
+end.