快救救我,已经搞了2天了????????(50分)

  • 主题发起人 marco_hsu
  • 开始时间
M

marco_hsu

Unregistered / Unconfirmed
GUEST, unregistred user!
目前我要用树状图作个产品的展开图,数据库设计如下
父产品 层次 子产品 子产品标记(判断是否还有下级产品)
A 1(代表成品) A1 0('O'代表还有下级)
A 1 A2 0
B 1 B1 0
A1 2 (半成品) A11 0
A2 2 A21 1(表示已没下级了)
B1 2 B11 1
A11 3 A111 1
A11 3 A112 1
A11 3 A113 1
............
表示成
A------|--A1----A11--|-A111
| |-A112
| |-A113
|--A2____A21

B---------B1----B11
并且还会有很多很多种原料,我试了很多种方法,可都不凑效。恳请大家指点指点(包括数据表的设计),谢谢!!!
 
很多论坛的树形结构和数据库表结构都和你差不多,可以借鉴借鉴。
 
你是谁,我好象认识你!!!
 
to wlmmlw:你好,能说的具体点吗,我真的好急呀
to goodyang:我刚刚加入这个论坛,认识我,是吗?
 
这是我刚作的一段程序,可老是出错,我是用一个字符串和节点数组来记录还有下级产品的节点值
和他的父节点,如果记录中为0代表全是原材料无下级材料了。可就在这出错(好像无法改变循环变量)
while Tempint<>0 do //判断是否应该结束
begin
for I:=0 to Tempint-1 do
begin
I_node:=0;
With Prostr_DM.Qry_treeson do
begin
Close;
Sql.Clear ;
Sql.Add('Select son_no,son_sign from Prostr_table where Par_no= :pno');
Params[0].Value:=sonlist.strings;
Prepare;
Open;
if Fields[0].Value<>null then
begin
While not Eof do
begin
Tempstr:=Fields[0].AsString;
Tempnode:=Pro_tree.Items.AddChild(Tnodelist,Tempstr);
if fields[1].AsString='0' then
begin
Sonlist1.Add(Tempstr);
Tnodelist1[I_node]:=Tempnode;
I_node:=I_node+1;
end;
next;
end; //end While not Eof do
end
Else
begin
Sonlist1:=nil;
Tnodelist1[0]:=nil;
end;
end; //With Prostr_DM.Qry_treeson do
end;// for I:=0 to Sonlist.Count-1 do
// Sonlist:=Sonlist1;
Sonlist.Free;
Sonlist:=Tstringlist.Create ;
For I:=0 to Sonlist1.Count-1 do
Sonlist.Add(Sonlist1.Strings);
For I:=0 to 49 do
begin
Tnodelist:=Tnodelist1;
end;
Tempint:=sonlist.Count;
end;
// until Tempint=0;
// end;// while Sonlist.Count<>0 do
end;
 
你的数据库设计可以,查找数据是按关键字段(编号)排序,取得数据就为树型结构。
用树型控件输出既可!
 
这是有父级编码的树,很简单的
给你个例子
//按上级编码建树 fbjbm为本级编码,fsjbm为上级编码,fbjmc为本级名称,fexbm为扩展内部编码
procedure CreateTree(PQuery:TDataSet;fbjbm,fsjbm,fbjmc:string;Separator:string);
//-----------------------------------------------------------------------------
procedure TTreeFrame.CreateTree(PQuery: TDataSet; fbjbm, fsjbm,
fbjmc: string;Separator:string);
var
code,bh:string;
FNode:pchar;
i:integer;
str:string;
lTreeNode,lTreeNode1:TTreeNode;
begin
Clear;
//*******************************************
if not pquery.Active then exit;
//创建第一层
str:=fsjbm +'= ''00''';
pquery.Filter:=str;
pquery.Filtered:=true;

pquery.First;
while not pquery.Eof do
begin
bh:=pquery.fieldbyname(fbjbm).asstring;
code:=pquery.fieldbyname(fbjmc).asstring;
try
getmem(FNode,length(bh)+1);//得到指针的内存空间
except
exit;
end;
if Separator <> '' then code:=bh + Separator + code;
if length(code) >255 then code :=copy(code,0,255);

move(bh[1],FNode^,length(bh)+1);//指针指出结点对应的编码
treeview.Items.AddObject(nil,code, FNode);
pquery.Next;
Application.ProcessMessages;
end;
lTreeNode:=TreeView.Items.GetFirstNode;
while lTreeNode<>nil do
begin
FNode:=lTreeNode.Data;
str:=fsjbm+' = '+#39+FNode+#39;
pquery.Filter:='';
pquery.Filtered:=false;
pquery.Filter:=str;
pquery.Filtered:=true;
pquery.First;
while not pquery.Eof do
begin
bh:=pquery.fieldbyname(fbjbm).asstring;
code:=pquery.fieldbyname(fbjmc).asstring;
try
getmem(FNode,length(bh)+1);//得到指针的内存空间
except
exit;
end;
if Separator <> '' then code:=bh + Separator + code;
if length(code) >255 then code :=copy(code,0,255);

move(bh[1],FNode^,length(bh)+1);//指针指出结点对应的编码
treeview.Items.AddChildObject(lTreeNode, code, FNode);
pquery.Next;
Application.ProcessMessages;
end;//while do
lTreeNode:=lTreeNode.GetNext;
end;//while do
end;
如果数据量比较大,建议用分层建树
如有问题就联系我
 
改的网上的代码,你看看吧,数据库设计和你差不多
////////////////////////////////////
// Read Data from DB to TreeView
////////////////////////////////////
function TForm1.AddANode(aData: TMyData): TTreeNode; //加入一个结点到树TreeView1中
var
aNode: TTreeNode;
i : Integer;
begin
aNode := nil;
if aData.Added then //如果结点已经被加入则在树中找到此结点
begin
aNode := TreeView1.Items.GetFirstNode();
while aData <> TMyData(aNode.Data) do
aNode := aNode.GetNext();
Result := aNode;
Exit;
end;

if Trim(aData.Parent) <> '' then //如果有父结点,先则加入父结点
for i := 0 to MyDatas.Count - 1 do
if TMyData(MyDatas).Node = aData.Parent then
begin
aNode := AddANode(TMyData(MyDatas));
break;
end;

Result := TreeView1.Items.AddChildObject(aNode,
aData.NodeName + '-' + aData.Node, aData); //加入结点
aData.Added := True; //置标志状态
end;

procedure TForm1.BuildTree;
var
i : integer;
begin
TreeView1.Items.BeginUpdate;
TreeView1.Items.Clear;
LoadNodes;
for i := 0 to MyDatas.Count - 1 do
if not TMyData(MyDatas).Added then
AddANode(MyDatas);

TreeView1.Items.EndUpdate;
end;

procedure TForm1.LoadNodes;
var
aData: TMyData;
i : integer;
s: string;
begin
if not Assigned(MyDatas) then
MyDatas := TList.Create
else
begin //释放旧的数据
for i := 0 to MyDatas.Count - 1 do
begin
aData := MyDatas;
aData.Free;
end;
MyDatas.Clear;
end;

with DataModule2 do
begin
if not ADO_Sort.Active then ADO_Sort.Open; //打开存放结点的表

ADO_Sort.First;
while not ADO_Sort.Eof do
begin
aData := TMyData.Create;
s := ADO_Sort.FieldByName('SortNo').AsString;
aData.Node := s;
SetLength(s, Length(s) - 1);
aData.Parent := s;
aData.Added := false;
aData.NodeName := ADO_Sort.FieldByName('SortName').AsString;
MyDatas.Add(aData);
ADO_Sort.Next;
end;
ADO_Sort.Close;
end;
end;

//--------------------------------------------------
 
用树型结构表示科目代码的一种高效算法
在很多常见的财务软件中,科目代码一般都用树型结构来显示。要实现这一点,通常的做法是用多个(嵌套)循环,甚至递归等算法,将科目表中的代码”织”成树,但这样不但算法复杂,而且执行效率低。本人在实际的开发应用中,摸索出一种简单高效的算法,在此和盆托出,只在抛砖引玉,找出最佳解决方案。下面介绍在Delphi中的实现方法。

一.表结构

首先建立如下结构的数据表Code.DB,并输入一些测试数据:

字段名 类型 长度 说明
aCode 字符型 20 科目代码
aName 字符型 30 科目代码名称
...... ...... ...... ......
表(一)

其中,科目代码aCode的数据类型一定要字符型(一定),长度按具体要求而定,假如要支持六级编码,且代码结构是”3-2-2-2-2-2”,则该字段的长度不小于18,而其他字段则不作要求 。另外,要为字段aCode建一索引(切记),因为要用它来排序。

二.编写程序

1.新建一Project:CodeTree.drp,主窗体命名为frmMain,单元存为Main.Pas。在frmMain上添加一TtreeView控件,命名为tveCode,一个TImageList,命名为imgIcon,并装入三个Icon和Bmp,最后添加一Ttable控件,命名tblCode。frmMain和各控件的属性按表(二)设置:

组件 属性 设置
FrmMain Caption ’科目代码’
Font 宋体 9号
BorderStyle BsDialog
TvwCode Images ImgIcon
ReadOnly True
ImgIcon ImageList 装入三个图标
BtnClose Caption 关闭(C)

表(二)

2. 单元main.pas的完整源代码如下:

unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs,
Db, DBTables, ComCtrls, ImgList, StdCtrls;
type
TForm1 = class(TForm)
tvwCode: TTreeView;
tblCode: TTable;
ImageList1: TImageList;
btnClose: TButton;
procedure FormCreate(Sender: TObject);
procedure btnCloseClick(Sender: TObject);
private
{ Private declarations }
function LoadCode(crTbl:TDBDataSet):Integer;
function GetLevel(sFormat,sCode:String):Integer;
public
{ Public declarations }
end;
var
Form1: TForm1;
const
SCodeFormat = ’322222’; //科目代码结构
SFirstNodeTxt = ’科目代码’; //首节点显示的文字
implementation
{$R *.DFM}
//以下函数是本文的重点部分,
其主要功能是用一循环将Code.db表中的
//科目代码和科目代码名称显示出来
function TForm1.LoadCode(crTbl:TDBDataSet):Integer;
var NowID,sName,ShowTxt:String;
i,Level:Integer;
MyNode:array[0..6]of TTreeNode;
//保存各级节点,最长支持6级(重点)
begin
Screen.Cursor:=crHourGlass;
Level:=0;
With crTbl do
begin
try
if not Active then Open;
First;
tvwCode.Items.Clear;
//以下是增加第一项
MyNode[Level]:=tvwCode.Items.Add
(tvwCode.TopItem,SFirstNodeTxt);
MyNode[Level].ImageIndex:=0;
MyNode[Level].SelectedIndex:=0;
//以上是增加第一项
While Not Eof do
begin
NowID:=Trim(FieldByName(’aCode’).AsString);
ShowTxt:=NowID+’ ’+FieldByName(’aName’).AsString;
Level:=GetLevel(SCodeFormat,NowID);
//返回代码的级数
//以下是增加子项
//以下用上一级节点为父节点添加子节点
if Level>0 then//确保代码符合标准
begin
MyNode[Level]:=tvwCode.Items.AddChild
(MyNode[Level-1],ShowTxt);
MyNode[Level].ImageIndex:=1;
MyNode[Level].SelectedIndex:=2;
end;
//以上是增加子项
Next;
end;
finally
Close;
end;
end;
MyNode[0].Expand(False);//将首节点展开
Screen.Cursor:=crDefault;
end;
//以上函数将Code.db表中的科目代码和科目代码名称显示出来
//下面函数的功能是返回一代码的级数,
参数sFormat传递科目代码结构;
//参数sCode传递某一科目代码
function TForm1.GetLevel
(sFormat,sCode:String):Integer;
var i,Level,iLen:Integer;
begin
Level:=-1;//如果代码不符合标准,则返回-1
iLen:=0;
if (sFormat< >’’)and(sCode< >’’)then
for i:=1 to Length(sFormat) do
begin
iLen:=iLen+StrToInt(sFormat);
if Length(sCode)=iLen then
begin
Level:=i;
Break;
end;
end;
Result:=Level;
end;
//上面函数的功能是返回一代码的级数
procedure TForm1.FormCreate(Sender: TObject);
begin
with tblCode do
begin
DatabaseName:=ParamStr(1);
//使tblCode的DatabaseName指向应用程序所在的路径
TableName:=’Code.DB’; //指向数据表Code.DB
Open;
IndexFieldNames:=’aCode’;
//按字段aCode排序(不要漏掉)
end;
LoadCode(tblCode);
end;
procedure TForm1.btnCloseClick(Sender: TObject);
begin
Close;
end;
end.
其中,常量ScodeFormat是科目代码的代码结构,其定义的规则一定要和数据表Code..DB中的字段aCode的值相符。所以在实际应用中,让用户新增科目代码时,必须严格检查其规范性,只有完全符合事先定义的代码结构,才能添加入库。

函数GetLevel是求某一科目代码的级数,例如,有一科目代码”10102”,在代码结构是”322222”的情况下,调用函数GetLevel(’322222’,’10102’)将返回整数2 。

当然,本文的核心是LoadCode函数,该函数用了一个循环来遍历数据表Code.DB的所有记录,将字段aCode和aName的内容按层次显示出来。而在该函数中定义的二维数组MyNode[0..6],则显得优为重要,在这里,它作用类似于递归中的栈。因为在TTreeView添加子节点的方法AddChild(Node: TTreeNode; const S: string)中,要为其指定一父节点作为参数,而父节点的代码级数一定是要添加节点的代码级数减1,所以只要用一数组来动态保存和指定父节点就成功了。

三.运行结果

好了,现在把Code.DB复制到和可执行文件相同的目录下,按下F9键编译运行,本人运行的效果如图(一)。只要在以上的基础上加以完善,如增加维护功能,就可搬到实际应用中了。当然,本算法不单能用在科目代码上,其他类似的树型结构都能奏效。本人就已将此算法应用于[科目代码]、[物料清单(BOM)]、[库房管理]和[物料主文件]等多个模块中,取得令人满意的效果。


 
多人接受答案了。
 
顶部