• 动态RDLC报表(四)


    动态RDLC报表类DynamicReport:页眉、页脚节点数据填充

           我们就从页眉开始,页眉包含Logo、标题和标签三部分。


           首先要给页眉定一个高度,这个高度还必需是固定,定得高则页眉和主体之间空白太多不美观,定少又有部分内容显示不出来,偏偏这个页眉中的内容是不固定,那只能用一个参数 headerHeight 来记录了。后面的页脚也一样用一个参数footerHeight来记录。

    private float headerHeight = 0.0F;
    private float footerHeight = 0.0F;

           Logo部分大致包含一个Logo图片、企业名称、地址、电话、传真、邮箱和微信二维码,这些资料通过一个DataTable传递进来,然后通过AddLogo(DataTable dataTable, Boolean showLine)处理,保存到一个reportLogoString可变的字符序列里

    private StringBuilder reportLogoString = new StringBuilder();

           这个DataTable的数据后面还会用到,干脆就连公章、微信二维码、签名、人名、是不是审核一起传递进来,这个DataTable的结构如下:

                DataTable dt = new DataTable();
                dt.Columns.Add("OutletsLogo", Type.GetType("System.Byte[]"));
                dt.Columns.Add("OutletsName", Type.GetType("System.String"));
                dt.Columns.Add("OutletsAddress", Type.GetType("System.String"));
                dt.Columns.Add("OutletsTel", Type.GetType("System.String"));
                dt.Columns.Add("OutletsFax", Type.GetType("System.String"));
                dt.Columns.Add("OutletsEmail", Type.GetType("System.String"));
                dt.Columns.Add("OS", Type.GetType("System.Byte[]"));
                dt.Columns.Add("SignatureImg", Type.GetType("System.Byte[]"));
                dt.Columns.Add("EmployeeName", Type.GetType("System.String"));
                dt.Columns.Add("Reviewed", Type.GetType("System.String"));
                dt.Columns.Add("OutletsWeChat", Type.GetType("System.Byte[]"));
     

          这个DataTable需要增加到DataSets节点中,名称为DataLogo,保存在_reportItemPatterns中,然后添加Logo图片、企业名称、地址、电话、传真、邮箱和微信二维码并保存到reportLogoString中,然后根据需要来增加一条分隔线。

           需要注意设置左右边距、上边距和页眉高。

    1. private List<ReportItemPattern> _reportItemPatterns = new List<ReportItemPattern>();
    2. private StringBuilder reportLogoString = new StringBuilder();
    3. private Boolean isSignature = true;
    4. private Boolean isSignatureImg = false;
    5. public void AddLogo(DataTable dataTable, Boolean showLine)
    6. {
    7. if (dataTable != null && dataTable.Rows.Count > 0)
    8. {
    9. if (headerHeight == 0) headerHeight += TopMargin;
    10. var fields = new StringBuilder();
    11. foreach (DataColumn dataColumn in dataTable.Columns)
    12. {
    13. fields.AppendFormat("<Field Name=\"{0}\"><DataField>{0}DataField><rd:TypeName>" + dataColumn.DataType + "rd:TypeName>Field>", dataColumn.ColumnName);
    14. }
    15. var dataSetName = "DataLogo";
    16. var reportItemPattern = new ReportItemPattern();
    17. reportItemPattern.Data = DynamicReportExtension.RemoveZeroData(dataTable);
    18. reportItemPattern.DataSetName = dataSetName;
    19. reportItemPattern.DataSetString = reportItemPattern.DataSetPattern.Replace("@DataSetName", dataSetName).Replace("@Fields", fields.ToString());
    20. _reportItemPatterns.Add(reportItemPattern);
    21. float logoWidth = pageWidth - leftMargin * 2 - leftMargin / 2;
    22. float topPosition = topMargin;
    23. float leftPosition = leftMargin + leftMargin / 2;
    24. string imagesString = "";
    25. if (dataTable.Rows[0]["OutletsLogo"] != DBNull.Value)
    26. {
    27. string imgPattern = " <Image Name=\"LogoImg\">" +
    28. " <Source>EmbeddedSource>" +
    29. " <Value>LogoImgValue>" +
    30. " <MIMEType>image/pngMIMEType>" +
    31. " <Sizing>FitProportionalSizing>" +
    32. " <Top>" + topPosition + "cmTop>" +
    33. " <Left>" + leftPosition + "cmLeft>" +
    34. " <Height>1.6cmHeight>" +
    35. " <Width>1.6cmWidth>" +
    36. " <Style>" +
    37. " <Border><Style>NoneStyle>Border>" +
    38. " Style>" +
    39. " Image>";
    40. reportLogoString.Append(imgPattern);
    41. leftPosition += 1.7F;
    42. headerHeight += 1.7F;
    43. logoWidth -= 1.7F;
    44. imagesString += "<EmbeddedImage Name=\"LogoImg\"><MIMEType>image/pngMIMEType><ImageData>" + Convert.ToBase64String((Byte[])dataTable.Rows[0]["OutletsLogo"]) + "ImageData>EmbeddedImage>";
    45. }
    46. if (dataTable.Rows[0]["OutletsWeChat"] != DBNull.Value)
    47. {
    48. string imgPattern = " <Image Name=\"WeChatImg\">" +
    49. " <Source>EmbeddedSource>" +
    50. " <Value>WeChatImgValue>" +
    51. " <MIMEType>image/pngMIMEType>" +
    52. " <Sizing>FitProportionalSizing>" +
    53. " <Top>" + topPosition + "cmTop>" +
    54. " <Left>" + (pageWidth - leftMargin - 1.5) + "cmLeft>" +
    55. " <Height>1.5cmHeight>" +
    56. " <Width>1.5cmWidth>" +
    57. " <Style>" +
    58. " <Border><Style>NoneStyle>Border>" +
    59. " Style>" +
    60. " Image>";
    61. reportLogoString.Append(imgPattern);
    62. logoWidth -= 1.6F;
    63. imagesString += "<EmbeddedImage Name=\"WeChatImg\"><MIMEType>image/pngMIMEType><ImageData>" + Convert.ToBase64String((Byte[])dataTable.Rows[0]["OutletsWeChat"]) + "ImageData>EmbeddedImage>";
    64. }
    65. Image imageOs = null;
    66. Image imageSignature = null;
    67. if (dataTable.Rows[0]["OS"] != DBNull.Value) imageOs = BytesToImage((Byte[])dataTable.Rows[0]["OS"]);
    68. if (dataTable.Rows[0]["SignatureImg"] != DBNull.Value) imageSignature = BytesToImage((Byte[])dataTable.Rows[0]["SignatureImg"]);
    69. if (dataTable.Rows[0]["Reviewed"].ToString().ToUpper() == "TRUE")
    70. {
    71. isSignatureImg = true;
    72. Bitmap bitmap = CombinImage(imageOs, imageSignature, dataTable.Rows[0]["OutletsName"].ToString().Trim(), dataTable.Rows[0]["EmployeeName"].ToString().Trim(), fontString);
    73. imagesString += "<EmbeddedImage Name=\"SignatureImg\"><MIMEType>image/pngMIMEType><ImageData>" + Convert.ToBase64String(ImageToBytes(bitmap)) + "ImageData>EmbeddedImage>";
    74. }
    75. if (imagesString != "")
    76. {
    77. _docTemplate = _docTemplate.Replace("@LogoImageData", imagesString);
    78. }
    79. else
    80. {
    81. _docTemplate = _docTemplate.Replace("<EmbeddedImages>@LogoImageDataEmbeddedImages>", "");
    82. }
    83. if (dataTable.Rows[0]["OutletsName"].ToString() != "")
    84. {
    85. string namePattern = " <Textbox Name=\"LogoName\">" +
    86. " <CanGrow>trueCanGrow>" +
    87. " <KeepTogether>trueKeepTogether>" +
    88. " <Paragraphs>" +
    89. " <Paragraph>" +
    90. " <TextRuns>" +
    91. " <TextRun>" +
    92. " <Value>" + dataTable.Rows[0]["OutletsName"].ToString() + "Value>" +
    93. " <Style><FontFamily>" + fontString + "FontFamily><FontSize>16ptFontSize>Style>" +
    94. " TextRun>" +
    95. " TextRuns>" +
    96. " <Style>NoneStyle>" +
    97. " Paragraph>" +
    98. " Paragraphs>" +
    99. " <rd:DefaultName>LogoNamerd:DefaultName>" +
    100. " <Top>" + topPosition + "cmTop>" +
    101. " <Left>" + leftPosition + "cmLeft>" +
    102. " <Height>1.0cmHeight>" +
    103. " <Width>" + logoWidth + "cmWidth>" +
    104. " <ZIndex>1ZIndex>" +
    105. " <Style>" +
    106. " <VerticalAlign>BottomVerticalAlign>" +
    107. " <Border><Style>NoneStyle>Border>" +
    108. " <PaddingLeft>0ptPaddingLeft>" +
    109. " <PaddingRight>0ptPaddingRight>" +
    110. " <PaddingTop>0ptPaddingTop>" +
    111. " <PaddingBottom>0ptPaddingBottom>" +
    112. " Style>" +
    113. " Textbox>";
    114. reportLogoString.Append(namePattern);
    115. topPosition += 1.0F;
    116. if (headerHeight < 1.7F) headerHeight += 1.7F;
    117. }
    118. string address = "";
    119. if (dataTable.Rows[0]["OutletsAddress"].ToString() != "") address = dataTable.Rows[0]["OutletsAddress"].ToString();
    120. if (address != "")
    121. {
    122. if (dataTable.Rows[0]["OutletsTel"].ToString() != "") address += " " + dataTable.Rows[0]["OutletsTel"].ToString();
    123. if (dataTable.Rows[0]["OutletsFax"].ToString() != "") address += " " + dataTable.Rows[0]["OutletsFax"].ToString();
    124. if (dataTable.Rows[0]["OutletsEmail"].ToString() != "") address += " " + dataTable.Rows[0]["OutletsEmail"].ToString();
    125. float fontHeight = 0.5F;
    126. if (Graphics.FromHwnd(IntPtr.Zero).MeasureString(address, new Font(fontString, 8)).Width * 1.12 > MillimetersToPixelsWidth(logoWidth * 10) - 3)
    127. {
    128. fontHeight += 0.3F;
    129. headerHeight += 0.3F;
    130. }
    131. string addrPattern = " <Textbox Name=\"LogoAddr\">" +
    132. " <CanGrow>trueCanGrow>" +
    133. " <KeepTogether>trueKeepTogether>" +
    134. " <Paragraphs>" +
    135. " <Paragraph>" +
    136. " <TextRuns>" +
    137. " <TextRun>" +
    138. " <Value>" + address + "Value>" +
    139. " <Style><FontFamily>" + fontString + "FontFamily><FontSize>8ptFontSize>Style>" +
    140. " TextRun>" +
    141. " TextRuns>" +
    142. " <Style>NoneStyle>" +
    143. " Paragraph>" +
    144. " Paragraphs>" +
    145. " <rd:DefaultName>LogoAddrrd:DefaultName>" +
    146. " <Top>" + topPosition + "cmTop>" +
    147. " <Left>" + leftPosition + "cmLeft>" +
    148. " <Height>" + fontHeight + "cm Height>" +
    149. " <Width>" + logoWidth + "cmWidth>" +
    150. " <ZIndex>2ZIndex>" +
    151. " <Style>" +
    152. " <VerticalAlign>TopVerticalAlign>" +
    153. " <Border><Style>NoneStyle>Border>" +
    154. " <PaddingLeft>2ptPaddingLeft>" +
    155. " <PaddingRight>2ptPaddingRight>" +
    156. " <PaddingTop>0ptPaddingTop>" +
    157. " <PaddingBottom>2ptPaddingBottom>" +
    158. " Style>" +
    159. " Textbox>";
    160. reportLogoString.Append(addrPattern);
    161. topPosition += fontHeight + 0.05F;
    162. if (headerHeight < 1.7F) headerHeight += 1.7F;
    163. }
    164. if (topPosition > topMargin)
    165. {
    166. if (showLine)
    167. {
    168. string linePattern = " <Line Name=\"LineLogo\">" +
    169. " <Top>" + topPosition + "cmTop>" +
    170. " <Left>" + leftMargin / 2 + "cmLeft>" +
    171. " <Height>0.0cmHeight>" +
    172. " <Width>" + (pageWidth - leftMargin) + "cmWidth>" +
    173. " <ZIndex>3ZIndex>" +
    174. " <Style>" +
    175. " <Border><Style>SolidStyle>Border>" +
    176. " Style>" +
    177. " Line>";
    178. reportLogoString.Append(linePattern);
    179. }
    180. headerHeight += 0.1F;
    181. topMargin = topPosition + 0.1F;
    182. }
    183. }
    184. }

           在这里面,还生成了一个签名、盖章的图片,后面会调用到。图章的大小根据需要设定。

    1. public byte[] ImageToBytes(Image image)
    2. {
    3. System.IO.MemoryStream ms = new System.IO.MemoryStream();
    4. image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
    5. byte[] bytes = ms.GetBuffer();
    6. ms.Close();
    7. return bytes;
    8. }
    9. public Image BytesToImage(byte[] bytes)
    10. {
    11. System.IO.MemoryStream ms = new System.IO.MemoryStream(bytes);
    12. Image image = Image.FromStream(ms);
    13. ms.Close();
    14. return image;
    15. }
    16. public static Bitmap CombinImage(Image imgOS, Image imgSignature, string outletsName, string employeeName, string fontString)
    17. {
    18. int width = 143;
    19. int height = 143;
    20. int top = 90;
    21. if (imgOS != null)
    22. {
    23. if (imgOS.Width > 165)
    24. {
    25. width = 171;
    26. height = 116;
    27. top = 82;
    28. }
    29. }
    30. if (imgSignature != null)
    31. {
    32. if (imgSignature.Height < top) top = height - imgSignature.Height + 10;
    33. }
    34. Bitmap bmp = new Bitmap(300, height + 20);
    35. Graphics g = Graphics.FromImage(bmp);
    36. g.SmoothingMode = SmoothingMode.HighQuality;
    37. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    38. g.CompositingQuality = CompositingQuality.HighQuality;
    39. g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
    40. g.Clear(Color.Transparent);
    41. g.DrawLine(new Pen(Color.Black, 1), new Point(0, height - 10), new Point(300, height - 10));
    42. if (outletsName != "") g.DrawString(outletsName, new Font(fontString, 12), new SolidBrush(Color.Black), (300 - g.MeasureString(outletsName, new Font(fontString, 12)).Width) / 2, height - 11);
    43. if (imgSignature != null)
    44. {
    45. g.DrawImage(imgSignature, (300 - imgSignature.Width) / 2, top, imgSignature.Width, imgSignature.Height);
    46. }
    47. else
    48. {
    49. if (employeeName != "") g.DrawString(employeeName, new Font(fontString, 16), new SolidBrush(Color.Black), (300 - g.MeasureString(employeeName, new Font(fontString, 16)).Width) / 2, height - g.MeasureString(employeeName, new Font(fontString, 16)).Height - 10);
    50. }
    51. if (imgOS != null) g.DrawImage(imgOS, (300 - width) / 2, 1, width, height);
    52. GC.Collect();
    53. return bmp;
    54. }

           标签部分是不确定的,可以是多行单列或多行二列,数据好是通过一个DataTable传递进来。DataTable的结构为:

                DataTable dt = new DataTable();
                dt.Columns.Add("LeftName", Type.GetType("System.String"));
                dt.Columns.Add("LeftValue", Type.GetType("System.String"));
                dt.Columns.Add("RightName", Type.GetType("System.String"));
                dt.Columns.Add("RightValue", Type.GetType("System.String"));
           当需要使用一列标签时就只输入LeftName、LeftValue的值,RightName、RightValue留空;需要使用二列标签时则4个值都添加。

           传递进来的数据用前面定义的标签格式生成节点,保存到reportLabelPatterns中,然后根据需要来增加一条分隔线。需要留意处理页眉高。

    1. private List reportLabelPatterns = new List();
    2. public void AddLabel(DataTable dataTable, Color color, Boolean showLine)
    3. {
    4. if (dataTable != null && dataTable.Rows.Count > 0)
    5. {
    6. if (headerHeight == 0) headerHeight += TopMargin;
    7. float topPosition = topMargin;
    8. float leftPosition = leftMargin;
    9. float labelWidt = pageWidth - leftMargin * 2 - 0.31F;
    10. float aWidth = labelWidt * 0.3F;
    11. float bWidth = labelWidt * 0.7F;
    12. float cWidth = labelWidt * 0.22F;
    13. float dWidth = labelWidt * 0.23F;
    14. if (dataTable.Columns.Count > 2)
    15. {
    16. labelWidt = pageWidth - leftMargin - 0.61F;
    17. leftPosition = leftMargin / 2;
    18. aWidth = labelWidt * 0.2F;
    19. bWidth = labelWidt * 0.35F;
    20. }
    21. foreach (DataRow dataRow in dataTable.Rows)
    22. {
    23. var labelPattern = LabelPattern
    24. .Replace("@TopPosition", topPosition.ToString())
    25. .Replace("@TextboxName", reportLabelPatterns.Count.ToString())
    26. .Replace("@FontString", fontString)
    27. .Replace("@Color", color.Name.ToString())
    28. .Replace("@NameText", dataRow[0].ToString())
    29. .Replace("@NamePosition", leftPosition.ToString())
    30. .Replace("@NameWidth", aWidth.ToString())
    31. .Replace("@SplitPosition", (leftPosition + aWidth + 0.01F).ToString())
    32. .Replace("@ValuePosition", (leftPosition + aWidth + 0.32F).ToString())
    33. .Replace("@ValueWidth", bWidth.ToString());
    34. if (dataRow[1].ToString().Trim() != "")
    35. {
    36. labelPattern = labelPattern.Replace("@ValueText", dataRow[1].ToString());
    37. }
    38. else
    39. {
    40. labelPattern = labelPattern.Replace("@ValueText", "");
    41. }
    42. reportLabelPatterns.Add(labelPattern);
    43. if (dataTable.Columns.Count > 2)
    44. {
    45. labelPattern = LabelPattern
    46. .Replace("@TopPosition", topPosition.ToString())
    47. .Replace("@TextboxName", reportLabelPatterns.Count.ToString())
    48. .Replace("@FontString", fontString)
    49. .Replace("@Color", color.Name.ToString())
    50. .Replace("@NameText", dataRow[2].ToString())
    51. .Replace("@NamePosition", (leftPosition + aWidth + bWidth + 0.33F).ToString())
    52. .Replace("@NameWidth", cWidth.ToString())
    53. .Replace("@SplitPosition", (leftPosition + aWidth + bWidth + cWidth + 0.34F).ToString())
    54. .Replace("@ValuePosition", (leftPosition + aWidth + bWidth + cWidth + 0.65F).ToString())
    55. .Replace("@ValueWidth", dWidth.ToString());
    56. if (dataRow[3].ToString().Trim() != "")
    57. {
    58. labelPattern = labelPattern.Replace("@ValueText", dataRow[3].ToString());
    59. }
    60. else
    61. {
    62. labelPattern = labelPattern.Replace("@ValueText", "");
    63. }
    64. reportLabelPatterns.Add(labelPattern);
    65. }
    66. topPosition += 0.5F;
    67. headerHeight += 0.5F;
    68. if (Graphics.FromHwnd(IntPtr.Zero).MeasureString(dataRow[1].ToString(), new Font(fontString, 9)).Width * 1.12 > MillimetersToPixelsWidth(bWidth * 10) - 3)
    69. {
    70. headerHeight += 0.3F;
    71. }
    72. }
    73. if (showLine)
    74. {
    75. string linePattern = " LineLabel\">" +
    76. " " + (topPosition + 0.1F) + "cm" +
    77. " " + leftMargin / 2 + "cm" +
    78. " 0.0cm" +
    79. " " + (pageWidth - leftMargin) + "cm" +
    80. " 3" +
    81. " " +
    82. " " +
    83. " ";
    84. reportLabelPatterns.Add(linePattern);
    85. }
    86. headerHeight += 0.2F;
    87. topMargin = topPosition + 0.2F;
    88. }
    89. }

           标题部分比较简单,设置好样式就行。可以是添加多行标题,保存到_reportTitlePatterns中,同样要设置页眉高。

    1. private List _reportTitlePatterns = new List();
    2. public void AddTitle(string title, int fontSize, string fontWeight, Color color, TextAlign textAlign, float height)
    3. {
    4. if (!string.IsNullOrEmpty(title))
    5. {
    6. if (headerHeight == 0) headerHeight += TopMargin;
    7. var titlePattern = TitlePattern
    8. .Replace("@TextboxName", _reportTitlePatterns.Count.ToString())
    9. .Replace("@Title", title)
    10. .Replace("@FontString", fontString)
    11. .Replace("@FontSize", fontSize.ToString())
    12. .Replace("@FontWeight", fontWeight)
    13. .Replace("@Color", color.Name.ToString())
    14. .Replace("@TextAlign", textAlign.ToString())
    15. .Replace("@TopPosition", topMargin.ToString())
    16. .Replace("@LeftPosition", leftMargin.ToString())
    17. .Replace("@TextboxName", _reportTitlePatterns.Count.ToString())
    18. .Replace("@Height", height.ToString())
    19. .Replace("@Width", (pageWidth - leftMargin * 3).ToString());
    20. _reportTitlePatterns.Add(titlePattern);
    21. topMargin += height + 0.01F;
    22. headerHeight += height + 0.01F;
    23. }
    24. }

           页脚可以是多行的,保存到_reportPageFooter中,需要设置页脚的高度。

    1. private List<string> _reportPageFooter = new List<string>();
    2. public void AddPageFooter(string footerText, int fontSize, string fontWeight, Color color, TextAlign textAlign, float height)
    3. {
    4. if (!string.IsNullOrEmpty(footerText))
    5. {
    6. if (footerHeight == 0) footerHeight = 0.02F;
    7. string footerPattern = " <Textbox Name=\"Footer" + _reportPageFooter.Count.ToString() + "\">" +
    8. " <CanGrow>trueCanGrow>" +
    9. " <KeepTogether>trueKeepTogether>" +
    10. " <Paragraphs>" +
    11. " <Paragraph>" +
    12. " <TextRuns>" +
    13. " <TextRun>" +
    14. " <Value>" + footerText + "Value>" +
    15. " <Style><FontFamily>" + fontString + "FontFamily><FontSize>" + fontSize.ToString() + "ptFontSize><FontWeight>" + fontWeight + "FontWeight><Color>" + color.Name.ToString() + "Color>Style>" +
    16. " TextRun>" +
    17. " TextRuns>" +
    18. " <Style><TextAlign>" + textAlign.ToString() + "TextAlign>Style>" +
    19. " Paragraph>" +
    20. " Paragraphs>" +
    21. " <rd:DefaultName>Footer" + _reportPageFooter.Count.ToString() + "rd:DefaultName>" +
    22. " <Top>" + footerHeight.ToString() + "cmTop>" +
    23. " <Left>" + leftMargin.ToString() + "cmLeft>" +
    24. " <Height>" + height.ToString() + "cm Height>" +
    25. " <Width>" + (pageWidth - leftMargin * 2).ToString() + "cmWidth>" +
    26. " <ZIndex>1ZIndex>" +
    27. " <Style>" +
    28. " <VerticalAlign>MiddleVerticalAlign>" +
    29. " <Border><Style>NoneStyle>Border>" +
    30. " <PaddingLeft>2ptPaddingLeft>" +
    31. " <PaddingRight>2ptPaddingRight>" +
    32. " <PaddingTop>2ptPaddingTop>" +
    33. " <PaddingBottom>2ptPaddingBottom>" +
    34. " Style>" +
    35. " Textbox>";
    36. _reportPageFooter.Add(footerPattern);
    37. footerHeight += height + 0.01F;
    38. }
    39. }

    动态RDLC报表(一)

    动态RDLC报表(二)

    动态RDLC报表(三)

    动态RDLC报表(四)

    动态RDLC报表(五)

    动态RDLC报表(六)

    动态RDLC报表(七)

    动态RDLC报表完整实例下载

  • 相关阅读:
    【百度AI_人脸识别】图片对比相似度、人脸对比登录(调摄像头)
    《Java基础知识》Java 反射详解
    ElasticSearch - 解决ES的深分页问题 (游标 scroll)
    【1704. 判断字符串的两半是否相似】
    什么是微服务?
    java毕业设计体育训练队的信息管理系统服务端源码+lw文档+mybatis+系统+mysql数据库+调试
    MySQL 重复数据的处理
    探索ClickHouse——连接Kafka和Clickhouse
    Spring Data JPA 之如何自定义 Repository
    【低代码】为客户设计个性化方案:列表篇(客户自己调整排序对齐等)
  • 原文地址:https://blog.csdn.net/xgh815/article/details/126245109