Asp.net分页控件AspNetPager的使用(DataList和Reapter结合Linq的编辑与删除)
2011-08-24 11:51 夜雨瞳 阅读(934) 评论(0) 编辑 收藏 举报一、大概思想
1、数据交互形式
2、控件使用简述
二、数据库
CREATETABLE[dbo].[student](
[stu_id][int]IDENTITY(1,1) NOTNULL,
[stu_name][nvarchar](20) COLLATE Chinese_PRC_CI_AS NULL,
[stu_sex][nvarchar](1) COLLATE Chinese_PRC_CI_AS NULL,
CONSTRAINT[PK_student]PRIMARYKEYCLUSTERED
(
[stu_id]ASC
)WITH (PAD_INDEX =OFF, IGNORE_DUP_KEY =OFF) ON[PRIMARY]
) ON[PRIMARY]
三、新建网站
3.1、创建网站
Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ Register Assembly="AspNetPager" Namespace="Wuqi.Webdiyer" TagPrefix="webdiyer"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Asp.net中AspNetPager应用实例</title>
<link href="tablecloth/tablecloth.css" rel="stylesheet" type="text/css"/>
<script src="tablecloth/tablecloth.js" type="text/javascript"></script>
<style>
a
{
text-decoration: none;
color: #057fac;
}
a:hover
{
text-decoration: none;
color: #999;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<!-- 分页控件 -->
<asp:DataList ID="DataList1" runat="server" RepeatLayout="Flow" DataKeyField="stu_id"
OnDeleteCommand="lbtn_delete" OnEditCommand="lbtn_edit" OnCancelCommand="lbtn_cancel"
OnUpdateCommand="lbtn_upadte" OnItemDataBound="DataList1_ItemDataBound">
<HeaderTemplate>
<table cellspacing="0" cellpadding="0">
<tr>
<th colspan="4">
DataList使用
</th>
</tr>
<tr>
<th width="20%">
编号
</th>
<th>
学生
</th>
<th width="15%">
性别
</th>
<th width="15%">
操作
</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:Label ID="lbl_stu_id" runat="server" Text='<%#Eval("stu_id")%>'></asp:Label>
</td>
<td>
<asp:Label ID="lbl_stu_name" runat="server" Text='<%#Eval("stu_name") %>'></asp:Label>
</td>
<td>
<asp:Label ID="lbl_stu_sex" runat="server" Text='<%#Eval("stu_sex") %>'></asp:Label>
</td>
<td>
<asp:LinkButton ID="lbtn_edit" runat="server" CommandName="Edit">编辑</asp:LinkButton>
<asp:LinkButton ID="lbtn_delete" runat="server" CommandName="Delete">删除</asp:LinkButton>
</td>
</tr>
</ItemTemplate>
<EditItemTemplate>
<tr>
<td>
<asp:Label ID="stu_id" runat="server" Text='<%#Eval("stu_id")%>'></asp:Label>
</td>
<td>
<asp:TextBox ID="txt_stu_name" runat="server" Text='<%#Eval("stu_name") %>'></asp:TextBox>
</td>
<td>
<asp:Label ID="hidden_stu_sex" runat="server" Text='<%#Eval("stu_sex") %>' Visible="false"></asp:Label>
<asp:DropDownList ID="ddl_stu_sex" runat="server" DataTextField='<%#Eval("stu_sex") %>'>
<asp:ListItem>男</asp:ListItem>
<asp:ListItem>女</asp:ListItem>
</asp:DropDownList>
</td>
<td>
<asp:LinkButton ID="lbtn_update" runat="server" CommandName="Update">更新</asp:LinkButton>
<asp:LinkButton ID="lbtn_cancel" runat="server" CommandName="Cancel">取消</asp:LinkButton>
</td>
</tr>
</EditItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:DataList>
<!-- Repeater的使用 -->
<asp:Repeater ID="Repeater1" runat="server"
OnItemDataBound="Repeater1_ItemDataBound" onitemcommand="Repeater1_ItemCommand">
<HeaderTemplate>
<table cellspacing="0" cellpadding="0">
<tr>
<th colspan="4">
Repeater使用
</th>
</tr>
<tr>
<th width="20%">
编号
</th>
<th>
学生
</th>
<th width="15%">
性别
</th>
<th width="15%">
操作
</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<asp:Panel ID="pl_item" runat="server">
<tr>
<td>
<%#Eval("stu_id") %>
</td>
<td>
<%#Eval("stu_name") %>
</td>
<td>
<asp:Label ID="lbl_stu_sex" runat="server" Text='<%#Eval("stu_sex")%>'></asp:Label>
</td>
<td>
<asp:LinkButton runat="server" ID="lbtn_edit" CommandArgument='<%# Eval("stu_id")%>'
CommandName="Edit" Text="编辑"></asp:LinkButton>
<asp:LinkButton runat="server" ID="lbtn_delete" CommandArgument='<%# Eval("stu_id")%>'
CommandName="Delete" Text="删除"></asp:LinkButton>
</td>
</tr>
</asp:Panel>
<asp:Panel ID="pl_edit" runat="server">
<tr>
<td>
<asp:Label ID="stu_id" runat="server" Text='<%#Eval("stu_id")%>'></asp:Label>
</td>
<td>
<asp:TextBox ID="txt_stu_name" runat="server" Text='<%#Eval("stu_name") %>'></asp:TextBox>
</td>
<td>
<asp:Label ID="hidden_stu_sex" runat="server" Text='<%#Eval("stu_sex") %>' Visible="false"></asp:Label>
<asp:DropDownList ID="ddl_stu_sex" runat="server" DataTextField='<%#Eval("stu_sex") %>'>
<asp:ListItem>男</asp:ListItem>
<asp:ListItem>女</asp:ListItem>
</asp:DropDownList>
</td>
<td>
<asp:LinkButton runat="server" ID="lbtn_Update" CommandArgument='<%# Eval("stu_id")%>'
CommandName="Update" Text="更新"></asp:LinkButton>
<asp:LinkButton runat="server" ID="lbtn_Cancel" CommandArgument='<%# Eval("stu_id")%>'
CommandName="Cancel" Text="取消"></asp:LinkButton>
</td>
</tr>
</asp:Panel>
</ItemTemplate>
<FooterTemplate>
</table></FooterTemplate>
</asp:Repeater>
<webdiyer:AspNetPager CurrentPageButtonClass="cpb" ID="AspNetPagerStudent" runat="server"
FirstPageText="首页" LastPageText="尾页" NextPageText="下一页" PrevPageText="上一页" OnPageChanged="AspNetPagerStudent_PageChanged">
</webdiyer:AspNetPager>
</div>
</form>
</body>
</html>
Default.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
** 引用程序集
** BLL
******************************/
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
** 命名空间
******************************/
using BLL;
using System.Data;
publicpartialclass _Default : System.Web.UI.Page
{
///<summary>
/// 分页大小限制
///</summary>
privateint PageSize =5;
protectedvoid Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
/** 分页大小限制 **/
AspNetPagerStudent.PageSize = PageSize;
/** 绑定控件的记录数 **/
using (BLLStudent a =new BLLStudent())
{
AspNetPagerStudent.RecordCount = a.Select_StudentAllCount();
}
Binding();
}
}
///<summary>
/// 绑定数据
///</summary>
protectedvoid Binding()
{
using (BLLStudent k =new BLLStudent())
{
/** AspNetPager.StartRecordIndex默认为1,如果以pageSize=20,则第2页StartRecordIndex默认为21,第3页默认为41 **/
DataList1.DataSource = k.Select_StudentInformation(AspNetPagerStudent.StartRecordIndex, AspNetPagerStudent.PageSize);
DataList1.DataBind();
Repeater1.DataSource = k.Select_StudentInformation(AspNetPagerStudent.StartRecordIndex, AspNetPagerStudent.PageSize);
Repeater1.DataBind();
}
}
protectedvoid AspNetPagerStudent_PageChanged(object sender, EventArgs e)
{
Binding();
}
protectedvoid lbtn_cancel(object source, DataListCommandEventArgs e)
{
DataList1.EditItemIndex =-1;
Binding();
}
protectedvoid lbtn_delete(object source, DataListCommandEventArgs e)
{
int blo_id = Convert.ToInt32(DataList1.DataKeys[e.Item.ItemIndex]);
using (BLLStudent a =new BLLStudent())
{
if (!a.Delete_Student(blo_id))
{
Util.MessageBox.ShowMessage("删除失败");
}
}
AspNetPagerStudent.RecordCount -=1;
Binding();
}
protectedvoid lbtn_edit(object source, DataListCommandEventArgs e)
{
DataList1.EditItemIndex = e.Item.ItemIndex;
Binding();
}
protectedvoid lbtn_upadte(object source, DataListCommandEventArgs e)
{
ORM.student a =new ORM.student();
TextBox c=(TextBox)e.Item.FindControl("txt_stu_name");
DropDownList d = (DropDownList)e.Item.FindControl("ddl_stu_sex");
a.stu_id = Convert.ToInt32(DataList1.DataKeys[e.Item.ItemIndex]);
a.stu_name = c.Text;
a.stu_sex =char.Parse(d.SelectedValue);
using (BLLStudent dd =new BLLStudent())
{
if (!dd.Update_Student(a))
{
Util.MessageBox.ShowMessage("更新成功");
}
}
DataList1.EditItemIndex =-1;
Binding();
}
protectedvoid DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
{
/** 编辑dropdownList属性值 **/
if (e.Item.ItemType == ListItemType.EditItem)
{
DropDownList dd = (DropDownList)e.Item.FindControl("ddl_stu_sex");
Label a = (Label)e.Item.FindControl("hidden_stu_sex");
dd.SelectedValue = a.Text;
}
}
/** 以下为Repeater的用法 ******************************************************************/
///<summary>
/// 标志
///</summary>
privateint id =0;
///<summary>
/// Repeater的使用
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
protectedvoid Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
ORM.student rowv = (ORM.student)e.Item.DataItem;
string stu_id = Convert.ToString(rowv.stu_id);
if (stu_id != id.ToString())
{
((Panel)e.Item.FindControl("pl_item")).Visible =true;
((Panel)e.Item.FindControl("pl_edit")).Visible =false;
}
else
{
((Panel)e.Item.FindControl("pl_item")).Visible =false;
((Panel)e.Item.FindControl("pl_edit")).Visible =true;
Label a = (Label)e.Item.FindControl("hidden_stu_sex");
DropDownList d = (DropDownList)e.Item.FindControl("ddl_stu_sex");
d.SelectedValue = a.Text;
}
}
}
protectedvoid Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName =="Edit")
{
id =int.Parse(e.CommandArgument.ToString());
}
elseif (e.CommandName =="Cancel")
{
id =-1;
}
elseif (e.CommandName =="Update")
{
//Update.
ORM.student a =new ORM.student();
TextBox c = (TextBox)this.Repeater1.Items[e.Item.ItemIndex].FindControl("txt_stu_name");
DropDownList d = (DropDownList)this.Repeater1.Items[e.Item.ItemIndex].FindControl("ddl_stu_sex");
a.stu_id =int.Parse(e.CommandArgument.ToString());
a.stu_name = c.Text;
a.stu_sex =char.Parse(d.SelectedValue);
using (BLLStudent dd =new BLLStudent())
{
if (!dd.Update_Student(a))
{
Util.MessageBox.ShowMessage("更新失败");
}
}
Binding();
}
elseif (e.CommandName =="Delete")
{
//Delete.
int blo_id =int.Parse(e.CommandArgument.ToString());
using (BLLStudent a =new BLLStudent())
{
if (!a.Delete_Student(blo_id))
{
Util.MessageBox.ShowMessage("删除失败");
}
}
AspNetPagerStudent.RecordCount -=1;
}
Binding();
}
}
3.2、创建BLL类库
BLLStudent.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
** 引用程序集
** DAL
******************************/
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
** 命名空间
******************************/
using DAL;
namespace BLL
{
publicclass BLLStudent : IDisposable
{
///<summary>
/// 返回错误信息
///</summary>
publicstaticstring error =null;
#region###查询所有学生数目
publicint Select_StudentAllCount()
{
try
{
return DALStudent.Select_Student();
}
catch (Exception e)
{
error = e.Message;
return0;
}
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
******************************/
#region###查询所有学生信息
public IQueryable Select_StudentInformation(int start, int limit)
{
try
{
return DALStudent.Select_Student(start, limit);
}
catch (Exception e)
{
error = e.Message;
returnnull;
}
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
******************************/
#region###更新学生信息
publicbool Update_Student(ORM.student a)
{
try
{
DALStudent.Update_Student(a);
DALStudent.SubmitChanges();
returntrue;
}
catch (Exception e)
{
error = e.Message;
returnfalse;
}
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-23
******************************/
#region###删除学生信息
publicbool Delete_Student(int blo_id)
{
try
{
DALStudent.Delete_Student(blo_id);
DALStudent.SubmitChanges();
returntrue;
}
catch (Exception e)
{
error = e.Message;
returnfalse;
}
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-6
******************************/
#region###释放资源
publicvoid Dispose()
{
GC.SuppressFinalize(this);
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-6
******************************/
#region###析构函数
~BLLStudent()
{
this.Dispose();
}
#endregion
}
}
3.3、创建DAL类库
DALStudent.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
** 引用程序集
** ORM、System.Data.Linq
******************************/
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
** 命名空间
******************************/
using ORM;
using System.Data.Linq;
namespace DAL
{
publicclass DALStudent
{
///<summary>
/// 数据库连接,如果要修改数据库连接,则到“ORM->Properties->Settings.settings”进行修改
///</summary>
protectedstatic StudentDataContext db =new StudentDataContext();
///<summary>
/// 最大记录显示为1000条
///</summary>
privatestaticint maxLimit =1000;
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
******************************/
#region###查询所有学生信息
publicstaticint Select_Student()
{
int a = db.student.Count();
a = a > maxLimit ? maxLimit : a;
return a;
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-22
******************************/
#region###查询所有学生信息
publicstatic IQueryable Select_Student(int start, int limit)
{
start = start -1;
var q = (from o in db.student
select o).Skip(start).Take(limit);
/* 也可
var q = (from o in db.student
where o.stu_id !=null
select new {o.stu_id,o.stu_name,o.stu_sex}).Skip(start).Take(limit)
*/
return q;
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-23
******************************/
#region###更新学生信息
publicstaticvoid Update_Student(student a)
{
student b = db.student.Where(o => o.stu_id == a.stu_id).Single();
b.stu_name = a.stu_name;
b.stu_sex = a.stu_sex;
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-8-23
******************************/
#region###删除学生信息
publicstaticvoid Delete_Student(int blo_id)
{
student a = db.student.Where(o => o.stu_id == blo_id).Single();
db.student.DeleteOnSubmit(a);
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-9
******************************/
#region###提交更改数据
publicstaticvoid SubmitChanges()
{
try
{
db.SubmitChanges(System.Data.Linq.ConflictMode.ContinueOnConflict);
}
catch (System.Data.Linq.ChangeConflictException ex)
{
foreach (System.Data.Linq.ObjectChangeConflict occ in db.ChangeConflicts)
{
//以下是解决冲突的三种方法,选一种即可
// 使用当前数据库中的值,覆盖Linq缓存中实体对象的值
//occ.Resolve(System.Data.Linq.RefreshMode.OverwriteCurrentValues);
// 使用Linq缓存中实体对象的值,覆盖当前数据库中的值
//occ.Resolve(System.Data.Linq.RefreshMode.KeepCurrentValues);
// 只更新实体对象中改变的字段的值,其他的保留不变
occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges);
}
// 这个地方要注意,Catch方法中,我们前面只是指明了怎样来解决冲突,这个地方还需要再次提交更新,这样的话,值 //才会提交到数据库。
db.SubmitChanges();
}
}
#endregion
}
}
3.4、创建ORM类库
3.5、创建Util类库
JsonHelper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-3
** 引用程序集
** System.Web.Extensions、System.Runtime.Serialization
** .Net Framework3.5版本(.net Framework4.0版本已经存在于System.Runtime.Serialization)还需要加入:
** System.ServiceModel.Web的程序引用集
******************************/
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-7
** 命名空间
******************************/
using System.Web.Script.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
namespace Util
{
///<summary>
/// Json序列化的帮助类
///</summary>
publicstaticclass JsonHelper
{
/*****************************
** 作者: ruby_matlab
** 变更时间: 2011-6-6
******************************/
#region###将对象序列化为字符串
publicstaticstring Jso_ToJSON(thisobject tem_obj)
{
JavaScriptSerializer tem_serializer =new JavaScriptSerializer();
return tem_serializer.Serialize(tem_obj);
}
#endregion
/*****************************
** 作者: ruby_matlab
** 变更时间: 2011-6-6
******************************/
#region###将对象序列化为字符串
publicstaticstring Jso_ToJSON(thisobject tem_obj, int tem_recursionDepth)
{
JavaScriptSerializer tem_serializer =new JavaScriptSerializer();
tem_serializer.RecursionLimit = tem_recursionDepth;
return tem_serializer.Serialize(tem_obj);
}
#endregion
/*****************************
** 作者: ruby_matlab
** 变更时间: 2011-6-6
******************************/
#region###将字符串反序列化为对象
publicstaticobject JsonToObject(string jsonString, object obj)
{
DataContractJsonSerializer serializer =new DataContractJsonSerializer(obj.GetType());
MemoryStream mStream =new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
return serializer.ReadObject(mStream);
}
#endregion
/*****************************
** 作者: ruby_matlab
** 变更时间: 2011-6-6
******************************/
#region###将字符串反序列化为对象
publicstatic T Jso_DeJSON<T>(thisstring json)
{
JavaScriptSerializer serializer =new JavaScriptSerializer();
return serializer.Deserialize<T>(json);
}
#endregion
}
}
MessageBox.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-23
** 引用程序集
** System.Web
******************************/
namespace Util
{
///<summary>
/// 消息框
///</summary>
publicstaticclass MessageBox
{
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-23
******************************/
#region###提示消息
publicstaticvoid ShowMessage(string strMsg)
{
string str = String.Format("<script language='javascript'>window.alert('{0}');</script>", JsonHelper.Jso_ToJSON(strMsg));
System.Web.HttpContext.Current.Response.Write(str);
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-23
******************************/
#region###显示信息
publicstaticvoid ShowMessage(System.Web.UI.Page page, string strMsg)
{
string str = String.Format("<script language='javascript'>window.alert('{0}');</script>", JsonHelper.Jso_ToJSON(strMsg));
page.Response.Write(str);
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-23
******************************/
#region###显示信息并跳转Url
publicstaticvoid ShowMessage(string strMsg, string Url)
{
string str = String.Format("<script language='javascript'>window.alert('{0}');window.location.href ='{1}'</script>", JsonHelper.Jso_ToJSON(strMsg), JsonHelper.Jso_ToJSON(Url));
System.Web.HttpContext.Current.Response.Write(str);
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-23
******************************/
#region###显示信息并跳转Url
publicstaticvoid ShowMessage(System.Web.UI.Page page, string strMsg, string Url)
{
string str = String.Format("<script type=\"text/javascript\">window.alert('{0}');window.location.href ='{1}';</script>", JsonHelper.Jso_ToJSON(strMsg), JsonHelper.Jso_ToJSON(Url));
page.Response.Write(str);
}
#endregion
/******************************
** 作者: ruby_matlab
** 变更时间: 2011-6-23
******************************/
#region###确定信息
publicstaticvoid ShowConfirm(string strMsg, string strUrl_Yes, string strUrl_No)
{
string str = String.Format("<script language='javascript'> if(window.confirm('{0}')) { window.location.href='{1}' } else { window.location.href='{1}' };</script>",
JsonHelper.Jso_ToJSON(strMsg), JsonHelper.Jso_ToJSON(strUrl_Yes), JsonHelper.Jso_ToJSON(strUrl_No));
System.Web.HttpContext.Current.Response.Write(str);
}
#endregion
}
}
四、效果演示
五、总结
5.1、GridView、DataList、Repeater控件使用场景(具体可参考:http://www.cnblogs.com/iam_wangzhe/articles/1498055.html)
GridView适用场景:GridView 控件一般适用于以表的形式显示规则的二维关系数据。例如用于显示站点下所有用户的列表。
DataList适用场景:DataList 一般适用于显示“单列多行”关系的数据,例如用于显示产品列表,每一个ListItem显示一件产品的信息,你可以为ListItem 定义自己需要的格式。
Repeater适用场景:功能非常单薄,它使用数据源返回的一组记录呈现只读列表。
5.2、一般花些时间,还是可以用控件做出一些比较好看的效果,如果能够恰当地运用.net提供的控件,极大地方便我们的编程工作。
5.3、这里只作了DataList和Repeater这2方面的介绍,如果对GridView有兴趣也可以深入去学习,暂且不说了。
六、源代码提供
6.1、运行环境
系统:Win7
IDE工具:VS2008
版本:.net framework3.5
数据库:SQL2005
帮助文档:http://msdn.microsoft.com/zh-cn/library
6.2、代码下载
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步