标签:
1、Lable——标签:在网页中呈现出来的时候会变成span标签
属性:Text——标签上的文字
BackColor,ForeColor——背景色,前景色
Font——字体
Bold-加粗
Italic-倾斜
UnderLine-下划线 OverLine 上划线 StrikeOut 删除线
Name - 字体名
Size - 字体的大小
BorderColor——边框颜色
BorderWidth-边框粗细
BorderStyle - 边框样式
Height——高
Width——宽
Enabled-是否可用
Visible-是否可见
ToolTip-鼠标指上去的提示信息
CssClass - 样式表的class选择器
2.Literal——这也是一个文本标签,但它在网页中显示的时候不会变成span标签
它 的属性很少,是派生自control类的
3.TextBox——文本框 HiddenField——隐藏域 textarea--文本域
属性:它拥有Lable所有的属性
TextMode:文本框的呈现模式——SingleLine--单行文本框;MultiLine-多行文本框;Password-密码框
ReadOnly - 只读
MaxLength - 最大输入的字符数。只有TextMode是SingleLine和Password的时候起作用,在MultiLine的时候不起作用。
Columns:宽度,以字母个数为单位
Rows:高度,以行数为单位。只有TextMode是MultiLine的时候才起作用。在单行文本或多行文本下是不起作用的。
4.Button——按钮 LinkButton 超链接按钮 ImageButton 图片按钮(ImageUrl属性,需要把图片放项目文件夹中,如果VS没有显示出来,需要刷新下)
属性:它拥有Lable所有的属性
OnClientClick:当按钮被点击的时候,要执行的客户端的JS代码。它的触发要在按钮的C#事件代码之前。
5.HyperLink:超链接
属性:它拥有Lable所有的属性
NavigateUrl:超链接指向的地址,相当于href
Target:打开位置
ImageUrl:图片超链接的地址
6.Image——图片
属性:拥有Lable所有的属性
ImageUrl:图片的地址
二,复合控件
1.DropDownList——下拉列表 拥有Lable的所有属性
对于DropDownlist必须会做三件事
(1)把内容填进去
法一:逐项添加
private void FillList() { List<QuanxianData> list = new QuanxianDA().Select(); //QuanxianData qd=new QuanxianData(); //qd.Qname="请选择"; //qd.Qno="-1"; //list.Insert(0, qd);在第一项加上“请选择”的功能 foreach (QuanxianData data in list) { //DropDownList 控件中的每个可选项都是由 ListItem 元素定义的! ListItem item = new ListItem(); item.Text = data.Qname; item.Value = data.Qno; DropDownList1.Items.Add(item); } }
法二:数据绑定(用这种简便方法)
1 private void FillList2() 2 { 3 List<QuanxianData> list = new QuanxianDA().Select(); 4 DropDownList1.DataSource = list;//绑定数据源 5 DropDownList1.DataTextField = "Qname";//要显示哪一数据列 6 DropDownList1.DataValueField = "Qno";//要设置一个value值 7 DropDownList1.DataBind(); //最终执行绑定填充,不要漏掉 8 } 9 10 11 //linq连接数据库方法 12 13 public partial class _Default : System.Web.UI.Page 14 { 15 protected void Page_Load(object sender, EventArgs e) 16 { 17 testDataContext _context = new testDataContext(); 18 DropDownList1.DataSource= _context.Nation; 19 DropDownList1.DataTextField ="Name";//要显示哪一项内容 20 DropDownList1.DataValueField = "Code";// 21 DropDownList1.DataBind(); 22 //最终执行绑定填充,不要漏掉 23 } 24 }
技巧:如何给下拉列表加上“请选择”的功能
1.用以上代码中的方法
2.事先在下拉列表中设置静态的"请选择"列表项。然后再绑定或添加数据的时候在后面添加上。
如果采用数据绑定模式,默认情况下会把原有的项冲掉。需要设置AppendDataBoundItems属性为true。
3.所有的项都绑定或填加到下拉列表后,再写代码加上”请选择“的功能。
1 protected void Page_Load(object sender, EventArgs e) 2 { 3 FillList(); 4 ListItem li = new ListItem("请选择", "-1"); 5 DropDownList1.Items.Insert(0, li); 6 }
(2)把选中的值取出来:选择列表里的项,点击按钮,在标签中显示选择的内容
注:由于DropDownList需要打到服务器执行,需要把属性AutoPostBack(当内容更改时,自动发回到服务器)改为Ture;
1 protected void Button1_Click(object sender, EventArgs e) 2 { 3 Label1.Text = DropDownList1.SelectedItem.Text + DropDownList1.SelectedItem.Value;//取出选中项的文本和值 4 Label1.Text = DropDownList1.SelectedValue;//取出选中项的值 5 int index = DropDownList1.SelectedIndex;//取出选中项的索引号 6 Label1.Text = DropDownList1.Items[index].Text + DropDownList1.Items[index].Value;//取出对应索引号的文本和值 7 }
注意:每次点击按钮时,都是要先执行Page_Load中的代码,然后再执行Button-Click中的代码
所以在Page_Load中要加入一下代码
if(!IsPostBack)
{
防止每次点击提交页面都会执行这里面的代码。
这里面的代码,只有页面初次加载的时候才被执行。点击按钮提交的时候,不会被执行到。
以后记着:在Page_Load事件中99%的情况下需要写这段判断
}
(3)设定某项为选中项
给DropDownList的两个属性赋值:
SelectedIndex = 选中的索引号
SelectedValue = 选中项的值
protected void Button2_Click(object sender, EventArgs e) { //DropDownList1.SelectedIndex = Convert.ToInt32(TextBox1.Text); DropDownList1.SelectedValue = TextBox1.Text; }
2.RadioButtonList——单选按钮列表 Radiobutton--单选按钮
1.Radiobutton: 属性GroupName组名,同一个组名下的单选按钮产生互斥效果(例如:注册时选男、女)
例子:给2个Radiobutton设置同样的组名
属性:它拥有Dropdownlist所有的属性和功能
RepeatDirection:布局的方向
RepeatLayout:布局方式
RepeatColumns:一行显示几个
案例: 和Dropdownlist相同
3.CheckBoxList——复选框列表 CheckBox——复选框
1.CheckBox——复选框:属性:checked 是否选中;取值checkbox.text="";
拥有RadioButtonList所有的属性和功能
显示数据:
protected void Page_Load(object sender, EventArgs e) { DataClassesDataContext _conect = new DataClassesDataContext();//建立上下文连接对象 CheckBoxList1.DataSource = _conect.Nation; //获取数据源 CheckBoxList1.DataTextField = "Name";//要显示的项 CheckBoxList1.DataValueField = "Code";//返回值 CheckBoxList1.DataBind();//绑定数据源 }
技巧:
(1)如何获取选中的多个项?
//获取复选框的选中值。
//思路:遍历复选框列表中的每个项,判断每个项的选中情况。
foreach (ListItem li in CheckBoxList1.Items) { if (li.Selected) { Label2.Text += li.Value + ","; } }
2)如何设置几个项同时被选中
//设置文本框中指定的项(用 | 隔开的每一项)被选中
//思路:从文本框中解析出要选中的项的value值,然后再遍历每一项,判断是否是文本框中指定的,是的话就设为选中,不是就设为不选中。
CheckBoxList1.SelectedIndex = -1; //先清空列表中被选中的项 string s = TextBox1.Text; string[] ss = s.Split(‘|‘); //解析出要选中的value值 foreach (ListItem li in CheckBoxList1.Items) { if (ss.Contains(li.Value)) { li.Selected = true; continue; } }
4.ListBox——列表框
拥有Dropdownlist所有的属性和功能
SelectionMode - Single,Multiple
案例:如果是单选的话就照着Dropdownlist来做
如果是多选的话就照着Checkboxlist来做
5.RadioButtonList:单选按钮列表:
显示数据:
RadioButtonList1.DataSource = context.Nation;
RadioButtonList1.DataTextField = "Name";
RadioButtonList1.DataValueField = "Code";
RadioButtonList1.DataBind();
取选中项的值:
RadioButtonList1.SelectedValue.ToString();
设置选中项:
RadioButtonList1.SelectedIndex = 2;
6.验证控件
验证控件:
一、非空验证:RequiredFieldValidator
ErrorMessage:错误提示信息
ControlToValidate:要验证哪一个控件
Display:Static--不显示也占空间。Dynamic--不显示不占空间
InitialValue:初始值。
应用:
1.必须填
2.像“不能为空”,这种提示的形式。
二、对比验证:CompareValidator
ErrorMessage:
ControlToValidate:
Display:Static--不显示也占空间。Dynamic--不显示不占空间
ControlToCompare:要对比的控件。
ValueToCompare:要对比的值
Type:按照什么类型对比。输入的类型。
Operator:运算符
应用:
1.密码和确认密码——两个控件对比
2.月收入——控件和某个固定值对比。
三、范围验证: RangeValidator
ErrorMessage:
ControlToValidate:
Display:Static--不显示也占空间。Dynamic--不显示不占空间
Type:按照什么类型对比,需要输入的是什么类型
MaximumValue:范围的最大值
MinmumValue:范围的最小值
四、正则表达式验证:RegularExpressionValidator
ErrorMessage:
ControlToValidate:
Display:Static--不显示也占空间。Dynamic--不显示不占空间
RegularExpression:正则表达式
注意:正则表达式的使用与修改
五、自定义验证:CustomValidator
ErrorMessage:
ControlToValidate:
Display:Static--不显示也占空间。Dynamic--不显示不占空间
ClientValidationFunction:自定义的客户端验证函数
第一步:设置ClientValidationFunction属性
第二步:为ClientValidationFunction的属性编写JS代码
//像C#服务端事件函数一样,sender是事件源,args是事件数据
function checkSex(sender, bbb) {
//把要验证的值取出来。
var s = bbb.Value; //把验证控件要验证的那个控件(文本框)里面的值给取出来。
//进行验证
if (s == "男生" || s == "女生") {
//告诉系统,验证结果是否正确
bbb.IsValid = true;
}
else {
//告诉系统,验证结果是否正确
bbb.IsValid = false;
}
}
六、ValidationSummary:验证汇总控件
ShowMessageBox:是否以对话框的形式显示错误信息
ShowSummary:是否在页面上显示错误信息
七、验证分组:
把同一组的输入控件、按钮、验证控件的ValidationGroup属性设成一样的。
7、上传文件(图片)
ASP.NET 提供三种处理文件夹路径的工具:~ 操作符、Server.MapPath 方法以及 Href 方法。
~ 操作符
如需在编程代码中规定虚拟根目录,请使用 ~ 操作符。
如果您使用 ~ 操作符,而不是路径,就能够在不改变任何代码的情况下,将网站移动到不同的文件夹或位置:
var myImagesFolder = "~/images"; var myStyleSheet = "~/styles/StyleSheet.css";
Server.MapPath 方法
Server.MapPath 方法将虚拟路径 (/default.cshtml) 转换为服务器能够理解的物理路径 (C:\Johnny\MyWebSited\Demo\default.cshtml)。
当需要打开位于服务器上的数据文件时,您就会用到该方法(数据文件只能通过完整的物理路径来访问):
var pathName = "~/dataFile.txt"; var fileName = Server.MapPath(pathName);
在本教程的下一章中,您将学习更多有关在服务器上读取和写入数据文件的知识。
Href 方法
Href 方法把代码中的路径转换为浏览器能够理解的路径(浏览器无法理解 ~ 操作符)。
您使用 Href 方法来创建指向资源(比如图片和 CSS 文件)的路径。
您会经常在 HTML <a>、<img> 以及 <link> 元素中用到该方法:
@{var myStyleSheet = "~/Shared/Site.css";}
<!-- 创建指向 CSS 文件的链接 -->
<link rel="stylesheet" type="text/css" href="@Href(myStyleSheet)" />
<!-- 同上 : -->
<link rel="stylesheet" type="text/css" href="/Shared/Site.css" />
Href 方法属于 WebPage 对象的方法。
8.repeater
Repeater控件:展示数据库相关的数据
外观与数据分离。
外观代码:用模板来实现的。(头,脚,项,交替项,分隔符模板)
<asp:Repeater ID="Repeater1" runat="server">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><%# Eval("Name") %></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
数据代码:C#的绑定代码。
var query = _Context.Info;
Repeater1.DataSource = query;
Repeater1.DataBind();
Repeater在模板中绑定数据三种方式:
1.<%# Eval("属性名或列名","格式如{0:yyyy-MM-dd}")%>
2.<%# 函数名()%>,函数需要事先在.cs文件写好,并返回字符串。如:
public string ShowSexName()
{
bool sex = Convert.ToBoolean(Eval("Sex"));
return sex ? "男" : "女";
}
3.如果使用的实体类(如Linq),可扩展属性,在模板中使用<%# Eval("扩展属性")%>进行绑定。
Webfrom 生成流水号 组合查询 Repeater中单选与复选控件的使用 JS实战应用
Default.aspx 网页界面
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="Label1" runat="server" Text="流水号生成" Font-Bold="True" Font-Size="30px"></asp:Label> <br /> 代号:<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="生成流水号" /> <br /> 姓名:<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox> <br /> 性别:<asp:TextBox ID="TextBox3" runat="server"></asp:TextBox> <br /> 民族: <asp:TextBox ID="TextBox4" runat="server"></asp:TextBox> <br /> 生日: <asp:TextBox ID="TextBox5" runat="server"></asp:TextBox> <br /> <asp:Button ID="Button2" runat="server" Text="提交" /> <br /> <br /> </div> </form> </body> </html>
Defaule.aspx.cs C#界面
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class _Default : System.Web.UI.Page { private MyDBDataContext _Context = new MyDBDataContext(); protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { string prefix = "p" + DateTime.Now.ToString("yyyyMMdd"); //1查当天最大的流水号 int maxFlow = 0; var query = _Context.Info.Where(p=>p.Code.StartsWith(prefix)); if (query.Count() > 0) { query = query.OrderByDescending(p=>p.Code);//按流水号经序排列 string maxCode = query.First().Code;//获取最大流水号 maxFlow =Convert.ToInt32(maxCode.Substring(9)); } //组合查询最新的流水号 string flowCode = prefix + (maxFlow + 1).ToString("000"); //显示在文本框中 TextBox1.Text = flowCode; } }
*********************************************************************************************
组合查询
*********************************************************************************************
Default.aspx 网页界面
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <form id="form1" runat="server"> <h1>组合查询</h1> <div> 车名:<asp:TextBox ID="txtName" runat="server"></asp:TextBox> 系列:<asp:DropDownList ID="ddlBrand" runat="server" AppendDataBoundItems="True"> <asp:ListItem Value="-1">==请选择==</asp:ListItem> </asp:DropDownList> 油耗:<asp:TextBox ID="txtOil" runat="server"></asp:TextBox> <asp:Button ID="Button1" runat="server" Text="查询" OnClick="Button1_Click" /> <br /> <asp:Repeater ID="Repeater1" runat="server"> <HeaderTemplate> <ul> </HeaderTemplate> <ItemTemplate> <li> <%# Eval("Name") %> <font color="red"><%# Eval("Brand") %></font> <font color="blue"><%# Eval("Oil") %></font> </li> </ItemTemplate> <FooterTemplate> </ul> </FooterTemplate> </asp:Repeater> </div> </form> </body> </html>
Defaule.aspx.cs C#界面
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class Default2 : System.Web.UI.Page { private MyDBDataContext _Context = new MyDBDataContext(); private void FillBrand() { var query = _Context.Brand; ddlBrand.DataSource = query; ddlBrand.DataTextField = "Brand_Name"; ddlBrand.DataValueField = "Brand_Code"; ddlBrand.DataBind(); } protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { FillBrand(); } } //核心代码: protected void Button1_Click(object sender, EventArgs e) { var query1 = _Context.Car.AsQueryable(); //根据名称查 默认情况下应当是全集 var query2 = _Context.Car.AsQueryable();//根据系列查 var query3 = _Context.Car.AsQueryable();//根据油耗查 //根据输入框的填写情况来完善上面三个查询条件 if (txtName.Text.Trim().Length > 0) { query1 = query1.Where(p => p.Name.Contains(txtName.Text)); } if (ddlBrand.SelectedValue != "-1") { query2 = query2.Where(p => p.Brand == ddlBrand.SelectedValue); } if (txtOil.Text.Trim().Length > 0) { query3 = query3.Where(p => p.Oil == Convert.ToDecimal(txtOil.Text)); } //三个查询条件是逻辑与的关系,可以使用集合操作的交集来实现 var query = query1.Intersect(query2).Intersect(query3); //绑定显示 Repeater1.DataSource = query; Repeater1.DataBind(); } }
*********************************************************************************************
Repeater中单选与复选控件的使用 JS实战应用
*********************************************************************************************
Default.aspx 网页界面
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default3.aspx.cs" Inherits="Default3" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <script language="javascript"> function checkAll(ckall) { //找所有的每一项左侧的复选框。 var cks = document.getElementsByName("ck"); //遍历每个复选框,把每个复选框的选中状态设置得和chkall一样 for (var i = 0; i < cks.length; i++) { cks[i].checked = ckall.checked; } } function setCheckAll() { //思路:遍历所有项的复选框,判断是否全选。是全选的话,把ckall设为选中,否则把它设为不选中。 //1.找到所有的项的复选框。 var cks = document.getElementsByName("ck"); //2.遍历每个复选框,记录是否被全选了。 var selectedall = true; for (var i = 0; i < cks.length; i++) { selectedall = selectedall && cks[i].checked; } //3.根据是否被全选的状态,确定ckall的选中状态 var ckall = document.getElementById("ckall"); ckall.checked = selectedall; } function dodelete() { //1.找到所有的项的复选框。 var cks = document.getElementsByName("ck"); //2.判断是否有选中项 var selecteditem = false; for (var i = 0; i < cks.length; i++) { selecteditem = cks[i].checked || selecteditem; } //3.根据选中项的情况给出不同的对话框提示 if (selecteditem == false) { alert("请选择要删除的项"); return false; } else { var del = confirm("确认要删除选中的项吗?"); return del; } } </script> </head> <body> <form id="form1" runat="server"> <div> <asp:Repeater ID="Repeater1" runat="server"> <HeaderTemplate> <table width="100%" border="0" cellspacing="5"> <tr> <td> <input id="ckall" name="ckall" type="checkbox" onclick="checkAll(this)" /> </td> <td>名称</td> <td>系列</td> <td>厂商</td> <td>价格</td> </tr> </HeaderTemplate> <ItemTemplate> <tr> <td> <input id="ck_<%# Eval("Code") %>" name="ck" type="checkbox" value="<%# Eval("Code") %>" onclick="setCheckAll()" /> </td> <td><%# Eval("Name") %></td> <td><%# Eval("BrandName") %></td> <td><%# Eval("ProdName") %></td> <td><%# Eval("Price") %></td> </tr> </ItemTemplate> <FooterTemplate> </table> </FooterTemplate> </asp:Repeater> </div> <asp:Button ID="Button1" runat="server" OnClientClick="return dodelete()" OnClick="Button1_Click" Text="删除" /> <br /> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> <asp:Literal ID="Literal1" runat="server"></asp:Literal> </form> </body> </html>
Defaule.aspx.cs C#界面
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class Default3 : System.Web.UI.Page { private MyDBDataContext _Context = new MyDBDataContext(); protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { Show(); } } private void Show() { var query = _Context.Car; Repeater1.DataSource = query; Repeater1.DataBind(); } protected void Button1_Click(object sender, EventArgs e) { if (Request["ck"] != null) { //获取选中项的主键值 string s = Request["ck"].ToString(); //选中项的value值,使用,隔开的。 string[] keys = s.Split(‘,‘); //劈开上面的字符串,形成主键值的数组。 //根据上面的主键值删除数据 foreach (string key in keys) { var query = _Context.Car.Where(p=>p.Code == key); if (query.Count() > 0) { Car data = query.First(); _Context.Car.DeleteOnSubmit(data); _Context.SubmitChanges(); } } //刷新显示 Show(); Literal1.Text = "<script language=‘javascript‘>alert(‘删除成功‘)</script>"; } } }
使用 <AlternatingItemTemplate>
您可以在 <ItemTemplate> 元素后添加 <AlternatingItemTemplate> 元素,这样就可以描述交替行的外观了
eg
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <form id="form1" runat="server"> <div> <br /> <asp:Label ID="Label1" runat="server" Text="关键字:"></asp:Label> <asp:TextBox ID="txtName" runat="server"></asp:TextBox> <asp:Label ID="Label2" runat="server" Text="价格:"></asp:Label> <asp:TextBox ID="txtJGmin" runat="server" Width="49px"></asp:TextBox> <asp:Label ID="Label3" runat="server" Text="到"></asp:Label> <asp:TextBox ID="txtJGmax" runat="server" Width="49px"></asp:TextBox> <asp:Button ID="btnSelect" runat="server" OnClick="btnSelect_Click" Text="查询" /> <br /> <br /> <asp:Repeater ID="Repeater1" runat="server"> <HeaderTemplate> <table width="903" border="0" cellspacing="1" cellpadding="0" bgcolor="#6600FF"> <tr style="height:25px;"> <td width="260" bgcolor="#FFFFFF">名称</td> <td width="160" bgcolor="#FFFFFF">上市时间</td> <td width="160" bgcolor="#FFFFFF">油耗</td> <td width="160" bgcolor="#FFFFFF">功率</td> <td width="163" bgcolor="#FFFFFF">价格</td> </tr> </HeaderTemplate> <FooterTemplate> </table> </FooterTemplate> <ItemTemplate> <tr style="height:25px;"> <td bgcolor="#FFFFFF"><%#Eval("Name") %></td> <td bgcolor="#FFFFFF"><%#Eval("Time","{0:yyyy年MM月dd日}") %></td> <td bgcolor="#FFFFFF"><%#Eval("Oil") %></td> <td bgcolor="#FFFFFF"><%#Eval("Powers") %></td> <td bgcolor="#FFFFFF"><%#Eval("Price") %></td> </tr> </ItemTemplate> <AlternatingItemTemplate> <tr style="height:25px;"> <td bgcolor="gray"><%#Eval("Name") %></td> <td bgcolor="gray"><%#Eval("Time","{0:yyyy年MM月dd日}") %></td> <td bgcolor="gray"><%#Eval("Oil") %></td> <td bgcolor="gray"><%#Eval("Powers") %></td> <td bgcolor="gray"><%#Eval("Price") %></td> </tr> </AlternatingItemTemplate> </asp:Repeater> </div> </form> </body> </html>
标签:
原文地址:http://www.cnblogs.com/zyh-club/p/4877280.html