我程序里的一段,认真看看吧:
procedure TForm8.LoadTreeView;
var
strsql,tid:string;
begin
try DataModule3.ADOTable3.open;
DataModule3.ADOTable3.Locate('DTname',RzComboBox1.Text,[lopartialkey]);
tid:=inttostr(DataModule3.ADOTable3.FieldByName('id').value);
strsql:='select * from T where TypeID='+tid;
DQ.Active :=false;
DQ.SQL.Clear ;
DQ.SQL.Add(strsql);
DQ.Active :=true;
DQ.Filtered :=true;
DQ.Filter := 'Parent=0';
U_DiGui(0,seltv.TopItem );//从当前0层开始递归建树
except
showmessage('字典里没有数据!');
end;
end;
procedure TForm8.U_DiGui(parentID:Cardinal;ParentNode:TTreeNode);
var
tmpTBData:array of TableData;
i,j:integer;
tmpNode:TTreeNode;begin
j:=DQ.RecordCount;
setlength(tmpTBData,j);//保存递规上一层结点值
for i:=0 to j-1 do begin
tmpTBData[i].ID := Cardinal(DQ.fieldbyname('ID').value);
tmpTBData[i].Name := DQ.fieldbyname('Name').value;
tmpTBData[i].ParentID := Cardinal(DQ.fieldbyname('Parent').value);
DQ.Next;
end;
for i:=0 to j-1 do begin //递规调用建立所有结点
tmpNode:=seltv.Items.AddChild(ParentNode,tmpTBData[i].Name);
// tmpNode.ImageIndex:=2;
new(pData);
pData^.ID:=tmpTBData[i].ID;
tmpNode.Data:=pData;
DQ.Filter := 'Parent=' + IntToStr(Integer(tmpTBData[i].ID));
if DQ.RecordCount >0 then begin
U_DiGui(tmpTBData[i].ID,tmpNode );
end;
end;
end;
procedure TForm8.LoadTreeView;
var
strsql,tid:string;
begin
try DataModule3.ADOTable3.open;
DataModule3.ADOTable3.Locate('DTname',RzComboBox1.Text,[lopartialkey]);
tid:=inttostr(DataModule3.ADOTable3.FieldByName('id').value);
strsql:='select * from T where TypeID='+tid;
DQ.Active :=false;
DQ.SQL.Clear ;
DQ.SQL.Add(strsql);
DQ.Active :=true;
DQ.Filtered :=true;
DQ.Filter := 'Parent=0';
U_DiGui(0,seltv.TopItem );//从当前0层开始递归建树
except
showmessage('字典里没有数据!');
end;
end;
procedure TForm8.U_DiGui(parentID:Cardinal;ParentNode:TTreeNode);
var
tmpTBData:array of TableData;
i,j:integer;
tmpNode:TTreeNode;begin
j:=DQ.RecordCount;
setlength(tmpTBData,j);//保存递规上一层结点值
for i:=0 to j-1 do begin
tmpTBData[i].ID := Cardinal(DQ.fieldbyname('ID').value);
tmpTBData[i].Name := DQ.fieldbyname('Name').value;
tmpTBData[i].ParentID := Cardinal(DQ.fieldbyname('Parent').value);
DQ.Next;
end;
for i:=0 to j-1 do begin //递规调用建立所有结点
tmpNode:=seltv.Items.AddChild(ParentNode,tmpTBData[i].Name);
// tmpNode.ImageIndex:=2;
new(pData);
pData^.ID:=tmpTBData[i].ID;
tmpNode.Data:=pData;
DQ.Filter := 'Parent=' + IntToStr(Integer(tmpTBData[i].ID));
if DQ.RecordCount >0 then begin
U_DiGui(tmpTBData[i].ID,tmpNode );
end;
end;
end;
解决方案 »
- 一个数值类型的问题..string > 内存地址 ?
- 请问一下有关Wav文件头的格式,如何创建Wav文件。在线急
- 求函数或方法 现知道 一个控件的句柄 现在想得到这个控件的父窗口句柄
- 迷惘,MIS系统的报表应如何写??
- 如何按一定的格式导出成为一个WORD文件?
- 如何用Delphi控制SQL2000的:导入导出 备份与恢复 用户管理,分配权限等等
- 请问:如何在DLL中使用线程?
- 请问什么是属性空间?谢谢
- SQL SERVER2000 + Delphi6 的小数问题
- 为什么当调用鼠标Hook时,帝国时代会退出?
- 一个有关数据库的问题,请大家都来看看,会的请回答,不会的请UP一下。谢谢!!
- 我的dbcombobox的style属性是dropdown,readonly=true,但为什么不能编辑呢?
每次从库中调用时根据属性动态生成treenode
procedure TForm8.LoadTreeView;
var
strsql,tid:string;
begin
try DataModule3.ADOTable3.open;
DataModule3.ADOTable3.Locate('DTname',RzComboBox1.Text,[lopartialkey]);
tid:=inttostr(DataModule3.ADOTable3.FieldByName('id').value);
strsql:='select * from T where TypeID='+tid;
DQ.Active :=false;
DQ.SQL.Clear ;
DQ.SQL.Add(strsql);
DQ.Active :=true;
DQ.Filtered :=true;
DQ.Filter := 'Parent=0';
U_DiGui(0,seltv.TopItem );//从当前0层开始递归建树
except
showmessage('字典里没有数据!');
end;
end;
procedure TForm8.U_DiGui(parentID:Cardinal;ParentNode:TTreeNode);
var
tmpTBData:array of TableData;
i,j:integer;
tmpNode:TTreeNode;begin
j:=DQ.RecordCount;
setlength(tmpTBData,j);//保存递规上一层结点值
for i:=0 to j-1 do begin
tmpTBData[i].ID := Cardinal(DQ.fieldbyname('ID').value);
tmpTBData[i].Name := DQ.fieldbyname('Name').value;
tmpTBData[i].ParentID := Cardinal(DQ.fieldbyname('Parent').value);
DQ.Next;
end;
for i:=0 to j-1 do begin //递规调用建立所有结点
tmpNode:=seltv.Items.AddChild(ParentNode,tmpTBData[i].Name);
// tmpNode.ImageIndex:=2;
new(pData);
pData^.ID:=tmpTBData[i].ID;
tmpNode.Data:=pData;
DQ.Filter := 'Parent=' + IntToStr(Integer(tmpTBData[i].ID));
if DQ.RecordCount >0 then begin
U_DiGui(tmpTBData[i].ID,tmpNode );
end;
end;
end;
procedure TForm8.LoadTreeView;
var
strsql,tid:string;
begin
try DataModule3.ADOTable3.open;
DataModule3.ADOTable3.Locate('DTname',RzComboBox1.Text,[lopartialkey]);
tid:=inttostr(DataModule3.ADOTable3.FieldByName('id').value);
strsql:='select * from T where TypeID='+tid;
DQ.Active :=false;
DQ.SQL.Clear ;
DQ.SQL.Add(strsql);
DQ.Active :=true;
DQ.Filtered :=true;
DQ.Filter := 'Parent=0';
U_DiGui(0,seltv.TopItem );//从当前0层开始递归建树
except
showmessage('字典里没有数据!');
end;
end;
procedure TForm8.U_DiGui(parentID:Cardinal;ParentNode:TTreeNode);
var
tmpTBData:array of TableData;
i,j:integer;
tmpNode:TTreeNode;begin
j:=DQ.RecordCount;
setlength(tmpTBData,j);//保存递规上一层结点值
for i:=0 to j-1 do begin
tmpTBData[i].ID := Cardinal(DQ.fieldbyname('ID').value);
tmpTBData[i].Name := DQ.fieldbyname('Name').value;
tmpTBData[i].ParentID := Cardinal(DQ.fieldbyname('Parent').value);
DQ.Next;
end;
for i:=0 to j-1 do begin //递规调用建立所有结点
tmpNode:=seltv.Items.AddChild(ParentNode,tmpTBData[i].Name);
// tmpNode.ImageIndex:=2;
new(pData);
pData^.ID:=tmpTBData[i].ID;
tmpNode.Data:=pData;
DQ.Filter := 'Parent=' + IntToStr(Integer(tmpTBData[i].ID));
if DQ.RecordCount >0 then begin
U_DiGui(tmpTBData[i].ID,tmpNode );
end;
end;
end;
procedure TForm8.LoadTreeView;
var
strsql,tid:string;
begin
try DataModule3.ADOTable3.open;
DataModule3.ADOTable3.Locate('DTname',RzComboBox1.Text,[lopartialkey]);
tid:=inttostr(DataModule3.ADOTable3.FieldByName('id').value);
strsql:='select * from T where TypeID='+tid;
DQ.Active :=false;
DQ.SQL.Clear ;
DQ.SQL.Add(strsql);
DQ.Active :=true;
DQ.Filtered :=true;
DQ.Filter := 'Parent=0';
U_DiGui(0,seltv.TopItem );//从当前0层开始递归建树
except
showmessage('字典里没有数据!');
end;
end;
procedure TForm8.U_DiGui(parentID:Cardinal;ParentNode:TTreeNode);
var
tmpTBData:array of TableData;
i,j:integer;
tmpNode:TTreeNode;begin
j:=DQ.RecordCount;
setlength(tmpTBData,j);//保存递规上一层结点值
for i:=0 to j-1 do begin
tmpTBData[i].ID := Cardinal(DQ.fieldbyname('ID').value);
tmpTBData[i].Name := DQ.fieldbyname('Name').value;
tmpTBData[i].ParentID := Cardinal(DQ.fieldbyname('Parent').value);
DQ.Next;
end;
for i:=0 to j-1 do begin //递规调用建立所有结点
tmpNode:=seltv.Items.AddChild(ParentNode,tmpTBData[i].Name);
// tmpNode.ImageIndex:=2;
new(pData);
pData^.ID:=tmpTBData[i].ID;
tmpNode.Data:=pData;
DQ.Filter := 'Parent=' + IntToStr(Integer(tmpTBData[i].ID));
if DQ.RecordCount >0 then begin
U_DiGui(tmpTBData[i].ID,tmpNode );
end;
end;
end;
treeview1.items.addchilds
可以这样定义:
ATreeNode1, ATreeNode2: TTreeNode;增加结点A:
ATreeNode1 := TreeView.Items.Add(nil, 'A');
Add的返回值为当前增加的结点,此时ATreeNode1指向结点A在A下增加a:
ATreeNode2 := TreeView.Items.AddChild(ATreeNode1, 'a');
ATreeNode2指向a删除结点a:
TreeView.Delete(ATreeNode2);以上为基本用法,其他请参考Help或ComCtrls单元保存至数据库方法:
一个简单的方法:可根据树的层次建立字段名,如Field0表示顶层结点(第0层),Field1表示第1层结点...
或者采用数的存储方式建立字段名,对每个结点设定Parent,LeftChild,RightSibling等,访问时遍历即可。
根本无须递归就能实现各种树操作
每一个结点需唯一标识就行
能实现
但还在调试阶段
不能保证所有的功能都好用
所以源程序无法给你
SORRY
一個是主
一個是次
我用vb做過,現在也想用delphi做一個,二層的可以,多層的還沒成功.:)
我要,我要,我要,我还是要要要,先谢你了
我的email:[email protected]
//打开一个工程
Procedure TDBMain.OpenPrj(PrjID:Integer);
Var I,N:Integer;
ANode: TTreeNode;
NewNode:Boolean;
Begin
TreeView1.Enabled:=False;
TreeView1.Items.Clear; //工程名称
PubQuery.Close;
PubQuery.SQL.Clear;
PubQuery.SQL.Add('Select Name,NameC From DBMaster');
PubQuery.SQL.Add('Where ID='+IntToStr(PrjID));
PubQuery.Open; If PubQuery.FieldByName('Name').IsNull Then Exit; ANode:=TreeView1.Items.Add(Nil,PubQuery.FieldByName('NameC').AsString+'['+PubQuery.FieldByName('Name').AsString+']');
ANode.StateIndex:=PrjID; Try
NewNode:=True;
While NewNode Do
Begin
NewNode:=False;
N:=TreeView1.Items.Count;
For I:=0 To N-1 Do
If TreeView1.Items[I].Count=0 Then
Begin
PubQuery.Close;
PubQuery.SQL.Clear;
PubQuery.SQL.Add('Select * From DBMaster');
PubQuery.SQL.Add('Where UpID='+IntToStr(TreeView1.Items[I].StateIndex));
PubQuery.SQL.Add('Order By Ord');
PubQuery.Open; While Not PubQuery.Eof Do
Begin
ANode:=TreeView1.Items.AddChild(TreeView1.Items[I],PubQuery.FieldByName('NameC').AsString+'['+PubQuery.FieldByName('Name').AsString+']');
ANode.StateIndex:=PubQuery.FieldByName('ID').AsInteger;
If Not NewNode Then
NewNode:=True; PubQuery.Next;
End;
End;
End;
Finally
TreeView1.Enabled:=True;
End; TreeView1.FullExpand;
CurrentProjectID:=PrjID;
CurrentItemID:=-1;
End;
大家在学校学习的时候可能浮躁,觉得学课本没有。现在就用上了,你的问题只需要用树的静态链表的存储结构就可以了,具体的方法就按Impylm(韩冰) 和mahaixing(超级赛亚人)两位的方法就可以了。
cexo_wxf_ss(過路人) 的方法不是方法!
Focus(老鱼)说不要递归,我觉得没有必要不用,又不是不让你递归,你为什么不用?树的定义就是递归的,操作当然是递归方便了。
建议大家实际要联系理论,要有专业精神,然后才能有专业的技能,然后才能作出专业的东西,然后才能挣专业的钱,然后才能享受专业人才的生活。
话多了点,因为最近有感触,不是针对楼主。是好意。
{
TTreeView example
A SDP Code Tutorial....get inspired!This application demonstrates the use of the TTreeView component.
TTreeview is a very complicated component, only the
basic methods are shown in this demonstration.
See the online help for all the features.(c)BeenSoft 06-03-2000 All rights reserved.
[email protected]
http://surf.to/beensoft
}unit Unit1;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls;type
TForm1 = class(TForm)
TreeView1: TTreeView;
Edit1: TEdit;
Label1: TLabel;
Button1: TButton;
Button2: TButton;
SaveDialog: TSaveDialog;
OpenDialog: TOpenDialog;
Button3: TButton;
Button4: TButton;
GroupBox1: TGroupBox;
lblTreeNodeSelected: TLabel;
Label2: TLabel;
Label3: TLabel;
lblLevel: TLabel;
Label4: TLabel;
lblItemIndex: TLabel;
Label5: TLabel;
procedure Button1Click(Sender: TObject);
procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.DFM}
/////////////////////////////////////////////////////
// Add treenode to the selected treenode
////////////////////////////////////////////////////
procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.Items.AddChild(Treeview1.Selected,Edit1.Text);
Edit1.SetFocus; //shift focus to edit after Adding
end;/////////////////////////////////////////////////////
// TreeView Change event
// - Give information of the selected treenode
////////////////////////////////////////////////////
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);begin
lblTreeNodeSelected.Caption := TreeView1.Selected.Text;
lblItemIndex.Caption := IntToStr(TreeView1.Selected.Index);
lblLevel.Caption := IntToStr(TreeView1.Selected.Level);
end;/////////////////////////////////////////////////////
// Save the treeview to file
////////////////////////////////////////////////////
procedure TForm1.Button2Click(Sender: TObject);
begin
SaveDialog.Execute;
Treeview1.SaveToFile(SaveDialog.FileName);
end;/////////////////////////////////////////////////////
// Load the treeview from file
////////////////////////////////////////////////////
procedure TForm1.Button3Click(Sender: TObject);
begin
OpenDialog.Execute;
Treeview1.Items.Clear;
Treeview1.LoadFromFile(OpenDialog.FileName);
end;/////////////////////////////////////////////////////
// Delete the selected treenode
////////////////////////////////////////////////////
procedure TForm1.Button4Click(Sender: TObject);
begin
Treeview1.Selected.Delete;
end;end.
我就是这么做的
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, DBTables, ImgList, ComCtrls, Menus, StdCtrls;type
TForm1 = class(TForm)
codeTable: TTable;
CodeTreeView: TTreeView;
codeImageList: TImageList;
MainMenu1: TMainMenu;
readFileN: TMenuItem;
N1: TMenuItem;
fullExpandN: TMenuItem;
fullCollapseN: TMenuItem;
N5: TMenuItem;
selectExpandN: TMenuItem;
selectCollapseN: TMenuItem;
N2: TMenuItem;
addNodeN: TMenuItem;
addSubNodeN: TMenuItem;
N11: TMenuItem;
delNodeN: TMenuItem;
N3: TMenuItem;
prevNodeN: TMenuItem;
nextNodeN: TMenuItem;
sortN: TMenuItem;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure fullExpandNClick(Sender: TObject);
procedure selectExpandNClick(Sender: TObject);
procedure fullCollapseNClick(Sender: TObject);
procedure selectCollapseNClick(Sender: TObject);
procedure addNodeNClick(Sender: TObject);
procedure addSubNodeNClick(Sender: TObject);
procedure delNodeNClick(Sender: TObject);
procedure prevNodeNClick(Sender: TObject);
procedure nextNodeNClick(Sender: TObject);
procedure CodeTreeViewDragDrop(Sender, Source: TObject; X, Y: Integer);
procedure CodeTreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
private
function GetLevel(sFormat,sCode:String):Integer;
procedure loadCode(myTable:TDBDataSet);
procedure AppendNewItem(Node: TTreeNode; Topic: string);
public
{ Public declarations }
end;var
Form1: TForm1;
MaxNo: Integer;
const
SCodeFormat = '322222'; //科目代码结构
SFirstNodeTxt = '科目代码'; //首节点显示的文字implementation{$R *.DFM}procedure TForm1.AppendNewItem(Node: TTreeNode; Topic: string);
var
s:string;
begin
MaxNo:=codeTreeView.Items.Count ;
if codeTreeView.Items.Count=0 then MaxNo:=0;
inc(MaxNo);
if topic='' then s:=format('新标题 %d',[MaxNo]) else s:=Topic;
if Node=nil then // Node=nil 表示增加一个根结点
codeTreeView.Items.Add(nil,s)
else
codeTreeView.Items.AddChild(Node,s);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
with codeTable do
begin
DatabaseName:=ParamStr(1);
TableName:='Code.DB';
Open;
end;
LoadCode(codeTable); codeTreeView.DragMode :=dmAutomatic;
end;//以下函数是本文的重点部分,其主要功能是用一循环将Code.db表中的
//科目代码和科目代码名称显示出来
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[i]);
if Length(sCode)=iLen then
begin
Level:=i;
Break;
end;
end;
Result:=Level;
end;
//上面函数的功能是返回一代码的级数procedure TForm1.loadCode(myTable: TDBDataSet);
var
NowID,ShowTxt:String;
Level:Integer;
MyNode:array[0..6]of TTreeNode;
//保存各级节点,最长支持6级(重点)
begin
Screen.Cursor:=crHourGlass;
Level:=0;
With myTable do
begin
try
if not Active then Open;
First;
codeTreeView.Items.Clear;
//以下是增加第一项
MyNode[Level]:=codeTreeView.Items.Add(codeTreeView.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]:=codeTreeView.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传递某一科目代码procedure TForm1.fullExpandNClick(Sender: TObject);
begin
codeTreeView.FullExpand ;
end;procedure TForm1.selectExpandNClick(Sender: TObject);
begin
codeTreeView.Selected.Expand(true) ;
end;procedure TForm1.fullCollapseNClick(Sender: TObject);
begin
codeTreeView.FullCollapse ;
end;procedure TForm1.selectCollapseNClick(Sender: TObject);
begin
codeTreeView.Selected.Collapse(true);
end;procedure TForm1.addNodeNClick(Sender: TObject);
begin
if codeTreeView.Selected<>nil then AppendNewItem(codeTreeView.Selected.Parent,'')
else begin
AppendNewItem(nil,'');
end;
end;procedure TForm1.addSubNodeNClick(Sender: TObject);
begin
with codeTreeView.Selected do
if (not Expanded) and (GetFirstChild<>nil) then Expand(false);
//codeTreeView.Selected.Expand(true);
AppendNewItem(codeTreeView.Selected,'');end;procedure TForm1.delNodeNClick(Sender: TObject);
var
s:string;
node:TTreeNode;
begin
node:=codeTreeView.selected;
s:='['+node.Text+'] ';
if node.HasChildren then s:=s+'连同它的子项';
if MessageBox(handle,pchar('真的要删除'+s+'吗?'),pchar('删除节点'),mb_IconInformation+mb_YesNo+MB_DEFBUTTON2)=mrYes
then node.Delete ;
end;
procedure TForm1.prevNodeNClick(Sender: TObject);
begin
with codeTreeView do
if Selected.GetPrev<>nil then Selected:=Selected.GetPrev;
end;procedure TForm1.nextNodeNClick(Sender: TObject);
begin
with codeTreeView do
if Selected.GetNext<>nil then Selected:=Selected.GetNext;
end;procedure TForm1.CodeTreeViewDragDrop(Sender, Source: TObject; X,
Y: Integer);
var
AnItem,TempNode: TTreeNode;
begin
if codeTreeView.Selected = nil then Exit;
AnItem := codeTreeView.GetNodeAt(X, Y);
if (AnItem<>nil) and (AnItem<>codeTreeView.Selected) then
begin
TempNode:=codeTreeView.items.AddChild(AnItem,'');
codeTreeView.Selected.MoveTo(TempNode, naInsert); //插入节点在AnItem之前
TempNode.Delete;
end else
begin
codeTreeView.Selected.MoveTo(AnItem, naInsert); //插入节点在AnItem之前
end;
end;procedure TForm1.CodeTreeViewDragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
var
AnItem:TTreeNode;
begin
if Y<15 then // 自动上翻
begin
SendMessage(codeTreeView.Handle,WM_VSCROLL,SB_LINEUP,0);
codeTreeView.Repaint;
end
else
if Y>codeTreeView.Height-15 then
begin // 自动下翻
SendMessage(codeTreeView.Handle,WM_VSCROLL,SB_LINEDOWN,0);
codeTreeView.Repaint;
end
else // 自动展开
if Source=codeTreeView then
begin
AnItem := codeTreeView.GetNodeAt(X, Y);
Accept:=(AnItem<>nil) and (not codeTreeView.GetNodeAt(X,Y).HasAsParent(codeTreeView.Selected));
end;
end;end.
你能不能把你的源码发给我啊,这样看我看的头昏脑老涨的,结合程序界面看理清楚理解,还望兄台能成全在下,随时恭候:
我的EMAIL: [email protected]