精华内容
下载资源
问答
  • POI 导出 Word 表格

    千次阅读 2017-08-13 17:21:37
    项目需求,需要将页面上的报表导出Word文档。

    项目需求,需要将页面上的报表导出Word文档。


    一、报表如下:



    二、主要实现代码

    1.导出Util类

    package com.yhksxt.util;
    
    
    import java.io.IOException;
    import java.math.BigInteger;
    
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
    import org.apache.poi.xwpf.usermodel.XWPFDocument;
    import org.apache.poi.xwpf.usermodel.XWPFParagraph;
    import org.apache.poi.xwpf.usermodel.XWPFRun;
    import org.apache.poi.xwpf.usermodel.XWPFTable;
    import org.apache.poi.xwpf.usermodel.XWPFTableCell;
    import org.apache.poi.xwpf.usermodel.XWPFTableRow;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTJc;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
    
    
    public class ExprotSTUtil {
    
    
    
    
    
    public  void exportSTToWord(String titleName,String[][] list ,HttpServletResponse response) throws IOException{
    //创建document对象
    XWPFDocument document = new XWPFDocument();
    
    //添加标题
    XWPFParagraph titleParagraph = document.createParagraph();
    //设置段落居中  
           titleParagraph.setAlignment(ParagraphAlignment.CENTER); 
           XWPFRun titleParagraphRun = titleParagraph.createRun();  
           titleParagraphRun.setText(titleName);  
           titleParagraphRun.setColor("000000");  
           titleParagraphRun.setFontSize(20);  
    //创建表格
              int row = list.length +2;
              int column = list[0].length +3;
              XWPFTable table = document.createTable(row,column);
              setTableWidth(table, "10000"); 
           
              //处理表达合并和数据填充
              
              //合并列
              mergeCellsVertically(table, 0, 0, 1);
              mergeCellsVertically(table, 2, 0, 1);
              mergeCellsVertically(table, column-1, 0, 1);
              
              //合并行
              for(int i=0;i<row-2;i++){
              mergeCellsHorizontal(table, i, 0, 1);
                  mergeCellsHorizontal(table, i, 2, 4);
              }
              
              mergeCellsHorizontal(table, 0, 5, column-2);
              mergeCellsHorizontal(table, row-2, 0, 4);
              mergeCellsHorizontal(table, row-1, 0, 4);
              mergeCellsHorizontal(table, row-1, 5, column-1);
              
              //填充数据
              XWPFTableRow rowIndex = table.getRow(0);
              XWPFTableCell cell = rowIndex.getCell(0);
              cell.setText(list[0][0]);
              XWPFTableCell cell1 = rowIndex.getCell(2);
            cell1.setText(list[0][1]);
            XWPFTableCell cell2 = rowIndex.getCell(5);
            cell2.setText("试题类型及题量");
            XWPFTableCell cell3 = rowIndex.getCell(column-1);
            cell3.setText(list[0][list[0].length-1]);
             
            XWPFTableRow rowIndex1 = table.getRow(1);
            for(int j=5,i=2;j<column-1;j++,i++){
            XWPFTableCell cell4 = rowIndex1.getCell(j);
            cell4.setText(list[0][i]);
            }
             
            for(int i = 2,ii=1;i<row-1;i++,ii++){
            XWPFTableRow rowIndex2 = table.getRow(i);
            for(int j = 0,jj=0;j<column-1 ;j++){
            XWPFTableCell cell4 =null;
            if(j==0){
            cell4 =rowIndex2.getCell(j);
            cell4.setText(list[ii][jj]);
            jj++;
            }if(j==2){
            cell4 =rowIndex2.getCell(j);
            cell4.setText(list[ii][jj]);
            jj++;
            }else if(j>=5){
            cell4 =rowIndex2.getCell(j);
            cell4.setText(list[ii][jj]);
              jj++;
            }
            }
            }
             
            XWPFTableRow rowIndex3 = table.getRow(row-1);
            XWPFTableCell cell5 =rowIndex3.getCell(0);
            cell5.setText("其他需要说明的问题");
                  
              document.write(response.getOutputStream());
    }
    
     
    /***
    *  跨行合并  
    * @param table
    * @param col  合并列
    * @param fromRow 起始行
    * @param toRow   终止行
    */
    private void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {  
           for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {  
               XWPFTableCell cell = table.getRow(rowIndex).getCell(col);  
               if ( rowIndex == fromRow ) {  
                   // The first merged cell is set with RESTART merge value  
                   cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);  
               } else {  
                   // Cells which join (merge) the first one, are set with CONTINUE  
                   cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);  
               }  
           }  
       }
    
    /***
    * 跨列合并 
    * @param table
    * @param row 所合并的行
    * @param fromCell  起始列
    * @param toCell   终止列
    */
    private  void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {  
           for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {  
               XWPFTableCell cell = table.getRow(row).getCell(cellIndex);  
               if ( cellIndex == fromCell ) {  
                   // The first merged cell is set with RESTART merge value  
                   cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);  
               } else {  
                   // Cells which join (merge) the first one, are set with CONTINUE  
                   cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);  
               }  
           }  
       }  
       
    
    /***
    * 导出word 设置行宽
    * @param table
    * @param width
    */
    private  void setTableWidth(XWPFTable table,String width){  
            CTTbl ttbl = table.getCTTbl();  
            CTTblPr tblPr = ttbl.getTblPr() == null ? ttbl.addNewTblPr() : ttbl.getTblPr();  
            CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();  
            CTJc cTJc=tblPr.addNewJc();  
            cTJc.setVal(STJc.Enum.forString("center"));  
            tblWidth.setW(new BigInteger(width));  
            tblWidth.setType(STTblWidth.DXA);  
        }  
    }


    2.controller中测试方法

    @RequestMapping(value = "exprotWord")
    public void exprotWord(HttpServletRequest request,HttpServletResponse response,String judge,String titleName,String subjectId)
    throws IOException , Exception {
    String fileName =null;
    String[][] list = null;
    System.out.println(judge);
    if(judge.equals("tlbb")){
    list = questionService.getSubjectZhangTotal(subjectId);//页面所有显示内容
    titleName = titleName + "题量报表";
    }else if(judge.equals("blbb")){
    String[][] tllist = questionService.getSubjectZhangTotal(subjectId);
    list = questionService.getxkblbbBytlbb(tllist);//转成比例
    titleName = titleName + "比例报表";
    }else {
    return;
    }
    fileName = titleName+".docx";
    // 设置文件MIME类型
    response.setContentType(request.getServletContext().getMimeType(fileName));
    // 设置Content-Disposition
    response.setHeader("Content-Disposition",
    "attachment; fileName=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
    ExprotSTUtil exprotWord = new ExprotSTUtil();
    exprotWord.exportSTToWord(titleName, list, response);
    }



    三、导出效果




    四、值得探讨问题

    1.报表通过jsp页面显示,数据存储问题。

    报表牵涉到对数据的统计,往往比较复杂。本列中,对每个学科下,Question表中所有题统计。牵涉到Chapter表中改学科对应的章节、Theme表中该学科对应的题型。每个题又有专属的学科、章节。并且学科中的题型、章节都是管理员可以增添删除的,所有数据都是动态的,这也就增加了处理的难度。在不确定题都所属哪个章节、哪个章节下的题型。同时也是报表所需,必须统计所有该章节,题型。查出的题后台处理后添加。    业务逻辑相对复杂一点,所以最后不要把数据都传到前端,在前端处理。所以,我采取把所有显示看到的数据,后台处理,存储到二维数组中,前台遍历一下,全部显示。   刚刚入门,这肯定不是最好的方法,感觉有好的建议,希望大神们提出,给予指导,拜谢。

    2.导出word时,对所建的table合并单元格,该合并的单元格行列坐标为多少?(以至于怎么去赋值)

           细心的朋友,应该已经发现上面代码中有一个疑问。在我添加题量总和那一行时,是和上面一起遍历的。而我list【row-1】【0】= "题量总和",list【row-1】【1】= “”,但是我的表格此之间已经合并了。 合并后的表格数据是怎么传进去的,是两个list【row-1】【0】+ list【row-1】【1】值拼接后传进去的。还是单独取的list【row-1】【0】中的值。毕竟中在row-1行每一列都赋值一遍,而在此之前前两列已经合并。    肯定有明白的大神。 像我一样的小白,可以没事的时候自己测试下。 


    五、参考





    展开全文
  • Word表格之VBA知识

    千次阅读 多人点赞 2019-01-28 13:56:10
    Table对象(因为是对象,...下面是Table的常用方法(注意是部分,不是全部,只例出重要的方法,下面的属性皆如此,如果详细面全部的了解,请看Word VBA自带的帮助。VBAWD10.chm) 使用Table对象 可使用 Tables(...

    Table对象(因为是对象,所以用Set赋值)

    该对象代表一个单独的表格。Table 对象是Tables集合的一个成员。Tables集合包含了指定的选定内容范围文档中的所有表格

    下面是Table的常用方法(注意是部分,不是全部,只例出重要的方法,下面的属性皆如此,如果详细面全部的了解,请看Word VBA自带的帮助。VBAWD10.chm)

    使用Table对象

    可使用 Tables(index) 返回一个 Table 对象,其中 index 为索引号。索引号代表选定内容、范围或文档中表格的位置。下例将活动文档中的第一个表格转换为文本。

    ActiveDocument.Tables(1).ConvertToText Separator:=wdSeparateByTabs

    使用Add方法可以在指定范围内新增一表格。下例在活动文档的起始处添加一 3 x 4 表格。

    Set myRange = ActiveDocument.Range(Start:=0, End:=0)

    ActiveDocument.Tables.Add Range:=myRange, NumRows:=3, NumColumns:=4

    Cell 方法

    返回一个 Cell 对象,该对象代表表格中的一个单元格

    expression.Cell(Row, Column)

    expression      必需。该表达式返回一个Table对象。

    Row   Long 类型,必需。指返回的表格行数。可以是介于 1 和表格行数之间的任意整数。

    Column    Long 类型,必需。指返回的表格单元格数目。可以是介于 1 和表格列数之间的任意整数。

    示例

    本示例在新文档中创建一个 3x3 表格,并在表格的第一个和最后一个单元格中插入文本。

    Dim docNew As Document

    Dim tableNew As Table

    Set docNew = Documents.Add

    Set tableNew = docNew.Tables.Add(Selection.Range, 3, 3)

    With tableNew

        .Cell(1,1).Range.InsertAfter "First cell"

        .Cell(tableNew.Rows.Count, _

            tableNew.Columns.Count).Range.InsertAfter "Last Cell"

    End With

    本示例删除活动文档的第一个表格中的第一个单元格的内容。

    If ActiveDocument.Tables.Count >= 1 Then

        ActiveDocument.Tables(1).Cell(1, 1).Delete

    End If

    Split 方法

    在表格中紧靠指定行的上面插入一空段落,并且返回一个 Table 对象,此对象包含指定行及其下一行。(简单的的理解:就是指向拆分后的下面的表格,不清楚也没关系,看下面的例子。)

    expression.Split(BeforeRow)

    expression      必需。该表达式返回一个 Table 对象。

    BeforeRow     Variant 类型,必需。将要拆分的表格的前一行。可以为 Row 对象或行号。

    本示例在活动文档(应试是新建文档)中创建一张 5x5 的表格,并且在第三行之前进行拆分。然后为结果表格(新的 3x5 表格)的单元格添加底纹。

    Set newDoc = Documents.Add

    Set myTable = ActiveDocument.Tables.Add(Range:=Selection.Range, _

        NumColumns:=5, NumRows:=5)

    myTable.Split(BeforeRow:=myTable.Rows(3)).Shading _

        .Texture = wdTexture10Percent

    Table属性

    Range 属性

    本示例复制表格 1 中的首行。

     

    If ActiveDocument.Tables.Count >= 1 Then _

        ActiveDocument.Tables(1).Rows(1).Range.Copy

    End if

    Borders 属性

    该属性返回一个 Borders 集合,该集合代表指定对象的所有边框。

    expression.Borders

    expression      必需。该表达式返回“应用于”列表中的一个对象。

    示例

    本示例对活动文档中的第一个表格应用内部和外部边框。

    Set myTable = ActiveDocument.Tables(1)

    With myTable.Borders

        .InsideLineStyle = wdLineStyleSingle

        .OutsideLineStyle = wdLineStyleDouble

    End With

    Columns 属性

    返回一个 Columns 集合,该集合代表在某一区域、所选内容或表格中所有表格列。只读

    示例

    本示例显示活动文档的第一个表格中的列数。

    If ActiveDocument.Tables.Count >= 1 Then

        MsgBox ActiveDocument.Tables(1).Columns.Count

    End If

    本示例将当前列的宽度设置为 1 英寸。

    If Selection.Information(wdWithInTable) = True Then

        Selection.Columns.SetWidth ColumnWidth:=InchesToPoints(1), _

            RulerStyle:=wdAdjustProportional

    End If

    Rows 属性

    该属性返回一个 Rows 集合,该集合代表某个范围、所选部分或表格中所有的表格行。只读。

    本示例删除活动文档第一个表格的第二行。

    ActiveDocument.Tables(1).Rows(2).Delete

    本示例为插入点所在行的各单元格设置边框。

    Selection.Collapse Direction:=wdCollapseStart

    If Selection.Information(wdWithInTable) = True Then

        Selection.Rows(1).Borders.OutsideLineStyle =  wdLineStyleSingle

    Else

        MsgBox "The insertion point is not in a table."

    End If

    Column 对象

    代表单个表格列。Column 对象是 Columns 集合的一个元素。Columns 集合包括某一表格、选定内容或区域中的所有列。

    使用 Column 对象

    使用 Columns(index) 可返回单独的 Column 对象,其中 index 为索引序号。索引序号代表该列在 Columns 集合中的位置(从左至右计算)

    下列示例选定活动文档中的表格 1 的第一列。

    ActiveDocument.Tables(1).Columns(1).Select

    用 Cell 对象的 Column 属性可返回一个 Column 对象。下列示例删除单元格 1 中的文字,插入新文字,然后对该列进行排序。

    With ActiveDocument.Tables(1).Cell(1, 1)

        .Range.Delete

        .Range.InsertBefore "Sales"

        .Column.Sort

    End With

    用 Add 方法可在表格中添加一列。下列示例为活动文档的第一张表格中添加一列,然后将列宽设置为相等。

    If ActiveDocument.Tables.Count >= 1 Then

        Set myTable = ActiveDocument.Tables(1)

        myTable.Columns.Add BeforeColumn:=myTable.Columns(1)

        myTable.Columns.DistributeWidth

    End If

    说明

    用 Selection 对象的 Information 属性可返回当前列号。下列示例选定当前列并在消息框中显示其列号。

    If Selection.Information(wdWithInTable) = True Then

        Selection.Columns(1).Select

        MsgBox "Column " _

            & Selection.Information(wdStartOfRangeColumnNumber)

    End If

    Cell 对象

    代表单个表格单元格。Cell 对象是 Cells 集合中的元素。Cells 集合代表指定对象中所有的单元格。

    使用 Cell 对象

    用 Cell(row, column) 或 Cells(index)可返回 Cell 对象,其中 row 为行号,column 为列号,index 为索引序号。下列示例给第一行的第二个单元格加底纹。

    Set myCell = ActiveDocument.Tables(1).Cell(Row:=1, Column:=2)

    myCell.Shading.Texture = wdTexture20Percent

    下列示例给第一行的第一个单元格加底纹。

    ActiveDocument.Tables(1).Rows(1).Cells(1).Shading _

        .Texture = wdTexture20Percent

    用 Add 方法可在 Cells 集合中添加 Cell 对象。也可用 Selection 对象的 InsertCells 方法插入新单元格。下列示例在 myTable 的第一个单元格之前插入一个单元格。

    Set myTable = ActiveDocument.Tables(1)

    myTable.Range.Cells.Add BeforeCell:=myTable.Cell(1, 1)

    本示例将第一个表格的头两个单元格设定为一个域 (myRange)。区域设定之后,用 Merge 方法合并两个单元格。

    Set myTable = ActiveDocument.Tables(1)

    Set myRange = ActiveDocument.Range(myTable.Cell(1, 1) _

        .Range.Start, myTable.Cell(1, 2).Range.End)

    myRange.Cells.Merge

    说明

    使用带 Rows 或 Columns 集合的 Add 方法添加一行或一列单元格。

    使用 Selection 对象的 Information 属性返回当前行号和列号。下面的示例改变选中部分第一个单元格的宽度,再显示单元格的行号和列号。

    If Selection.Information(wdWithInTable) = True Then

        With Selection

            .Cells(1).Width = 22

            MsgBox "Cell " & .Information(wdStartOfRangeRowNumber) _

                & "," & .Information(wdStartOfRangeColumnNumber)

        End With

    End If

    Row 对象

    代表表格的一行。Row 对象是 Rows 集合中的一个元素。Rows 集合包括指定部分、区域或表格中的所有行。

    使用 Row 对象

    用 Rows(index) 可返回单独的 Row 对象,其中 index 为索引序号。索引序号代表该行在选定部分、区域或表格中的位置。下列示例删除活动文档中第一张表格的首行。

    ActiveDocument.Tables(1).Rows(1).Delete

    用 Add 方法可在表格中添加行。下列示例在选定部分首行前插入一行。

    If Selection.Information(wdWithInTable) = True Then

        Selection.Rows.Add BeforeRow:=Selection.Rows(1)

    End If

    说明

    用 Cells 属性可修改 Row 对象中的单个单元格。下列示例在选定部分中添加一张表格,并在表格第二行的各单元格内插入数字。

    Selection.Collapse Direction:=wdCollapseEnd

    If Selection.Information(wdWithInTable) = False Then

        Set myTable = _

            ActiveDocument.Tables.Add(Range:=Selection.Range, _

            NumRows:=3, NumColumns:=5)

        For Each aCell In myTable.Rows(2).Cells

            i = i + 1

            aCell.Range.Text = i

        Next aCell

    End If

    访问表格行或列时产生的错误

    如果要访问绘制表格中单独的行或列,而该表格又不统一,则会产生一个运行时错误。例如,如果活动文档中第一张表格的每列中具有不同数量的行,则使用下列指令将导致出错。

    Sub RemoveTableBorders()

        ActiveDocument.Tables(1).Rows(1).Borders.Enable = False

    End Sub

    要避免这种错误可首先使用 SelectColumn  SelectRow 方法选定一列或一行中的单元格。选定单元格后,再使用 Selection 对象的 Cells 属性。下列示例选定第一张文档表格中的第一行。Cells 属性用于访问选定的单元格(第一行中的所有单元格)以删除边框。

    Sub RemoveTableBorders()

        ActiveDocument.Tables(1).Cell(1, 1).Select

        With Selection

            .SelectRow

            .Cells.Borders.Enable = False

        End With

    End Sub

    下列示例选定第一张文档表格的第一列。For Each...Next 循环语句用于在所选内容(第一列中的所有单元格)的每个单元格中添加文字。

    Sub AddTextToTableCells()

        Dim intCell As Integer

        Dim oCell As Cell

        ActiveDocument.Tables(1).Cell(1, 1).Select

        Selection.SelectColumn

        intCell = 1

        For Each oCell In Selection.Cells

            oCell.Range.Text = "Cell " & intCell

            intCell = intCell + 1

        Next oCell

    End Sub

    处理表格

    创建一张表格,插入文字,并应用格式

    下列示例在活动文档的开头插入一张 4 列 3 行的表格。For Each...Next 结构用于循环遍历表格中的每个单元格。在 For Each...Next 结构中,InsertAfter 方法用于将文字添至表格单元格(单元格 1、单元格 2、以此类推)。

    Sub CreateNewTable()

        Dim docActive As Document

        Dim tblNew As Table

        Dim celTable As Cell

        Dim intCount As Integer

        Set docActive = ActiveDocument

        Set tblNew = docActive.Tables.Add( _

            Range:=docActive.Range(Start:=0, End:=0), NumRows:=3, _

            NumColumns:=4)

        intCount = 1

        For Each celTable In tblNew.Range.Cells

            celTable.Range.InsertAfter "Cell " & intCount

            intCount = intCount + 1

        Next celTable

        tblNew.AutoFormat Format:=wdTableFormatColorful2, _

            ApplyBorders:=True, ApplyFont:=True, ApplyColor:=True

    End Sub

    在表格单元格中插入文字

    下列示例在活动文档中第一张表格的第一个单元格中插入文字。Cell 方法返回单独的 Cell 对象。Range 属性返回一个 Range 对象。Delete 方法用于删除现有的文字,而 InsertAfter 方法用于插入文字“Cell 1,1”。

    Sub InsertTextInCell()

        If ActiveDocument.Tables.Count >= 1 Then

            With ActiveDocument.Tables(1).Cell(Row:=1, Column:=1).Range

                .Delete

                .InsertAfter Text:="Cell 1,1"

               ' .text="cell 1,1" 上面两行,可以用这一行表示。

            End With

        End If

    End Sub

    返回表格单元格中的文字,不包括表格结束单元格标记

    下列示例返回并显示文档中第一张表格的第一行中每个单元格的内容。

    Sub ReturnTableText()

        Dim tblOne As Table

        Dim celTable As Cell

        Dim rngTable As Range

        Set tblOne = ActiveDocument.Tables(1)

        For Each celTable In tblOne.Rows(1).Cells

            Set rngTable = ActiveDocument.Range(Start:=celTable.Range.Start, _

                End:=celTable.Range.End - 1)         '注意这里用了-1

            MsgBox rngTable.Text

        Next celTable

    End Sub

     

    Sub ReturnCellText()

        Dim tblOne As Table

        Dim celTable As Cell

        Dim rngTable As Range

        Set tblOne = ActiveDocument.Tables(1)

        For Each celTable In tblOne.Rows(1).Cells

            Set rngTable = celTable.Range

            rngTable.MoveEnd Unit:=wdCharacter, Count:=-1

            MsgBox rngTable.Text

        Next celTable

    End Sub

     

    将文本转换为表格

    下列示例在活动文档的开头插入用制表符分隔的文本,然后将这些文本转换为表格。

    Sub ConvertExistingText()

        With Documents.Add.Content

            .InsertBefore "one" & vbTab & "two" & vbTab & "three" & vbCr

            .ConvertToTable Separator:=Chr(9), NumRows:=1, NumColumns:=3

        End With

    End Sub

     

    返回每个表格单元格的内容

    下列示例定义一个数组,该数组的元素个数等于文档中第一张表格(假定为 Option Base 1)中的单元格数。For Each...Next 结构用于返回每个表格单元格的内容,并将文字指定给相应的数组元素。

    Sub ReturnCellContentsToArray()

        Dim intCells As Integer

        Dim celTable As Cell

        Dim strCells() As String

        Dim intCount As Integer

        Dim rngText As Range

        If ActiveDocument.Tables.Count >= 1 Then

            With ActiveDocument.Tables(1).Range

                intCells = .Cells.Count

                ReDim strCells(intCells)

                intCount = 1

                For Each celTable In .Cells

                    Set rngText = celTable.Range

                    rngText.MoveEnd Unit:=wdCharacter, Count:=-1

                    strCells(intCount) = rngText

                    intCount = intCount + 1

                Next celTable

            End With

        End If

    End Sub

    (测试环境.docVBA中有更好的方法,可以参考)

    将活动文档中的所有表格复制到新文档中

    本示例将当前文档中的表格复制到新文档中。

    Sub CopyTablesToNewDoc()

        Dim docOld As Document

        Dim rngDoc As Range

        Dim tblDoc As Table

        If ActiveDocument.Tables.Count >= 1 Then

            Set docOld = ActiveDocument

            Set rngDoc = Documents.Add.Range(Start:=0, End:=0)

            For Each tblDoc In docOld.Tables

                tblDoc.Range.Copy

                With rngDoc

                    .Paste

                    .Collapse Direction:=wdCollapseEnd

                    .InsertParagraphAfter

                    .Collapse Direction:=wdCollapseEnd

                End With

            Next

        End If

    End Sub

     

    以下为我对表格的认识:(陋见)

    在“测试环境.doc”中有不少的例子(在VBA中),也有解释,

    两个文档花了我8小时以上(即一个工作日以上)

    关于表格在VBA中的相关说明:

    1.         如下图,类似于回车 在VBA中也是chr(13),竖线就是chr(7),怎么知道的?

    2.         当然是看老大们知道的。不过。在“测试环境.doc”中有相关的宏能得到这些数字。这也是授之以渔吧。

    3.         重点推荐“测试环境.doc的相关代码用了我不少功夫,慢慢体会。不懂的可以提出来。

     

    Ch(13)

    Ch(7)

     

    4.         因为chr(13)为段落标记,所以在VBA中,ActiveDocument.Paragraphs.Count测得的段落数与工具、字数统计是不一样的。

    5.         如果这样统计:表格中单元格中类似的 且不为空就为一个段落,否则不算。这就与工工具、字数统计的段落数一样了。

    6.         如果要新建一个表格,再添加一些字符(包括数字)的话,更好的方法是:先字符写入文档中(当然,要加一些标记,以便确定单元格),再利用Word的表格、转换、文字转换为表格。这样,速度快一些。有以下的代码为证。

    Sub 表格5()

    '先放到文档,再放入表格

    Dim i%, astring As String

    Dim adoc As Document

    Dim atime As Long

    Application.ScreenUpdating = False '关闭屏幕更新

    atime = Timer '设atime为正前时间

    For i = 1 To 1000

        astring = astring & i & Chr(13)

    Next

        Set adoc = Documents.Add

            adoc.Content = astring

            adoc.Range.ConvertToTable Separator:=wdSeparateByParagraphs, NumColumns:=10, _

            NumRows:=100

    Application.ScreenUpdating = True

        MsgBox "先放到文档的运行时间为:" & Timer - atime

        '1.28,1.07,1.03

    End Sub

    Sub 表格6()

    '先生成表格,再向单元格中添数

    Dim i%, astring As String

    Dim adoc As Document

    Dim atime As Long

    Dim atable As Table

    Application.ScreenUpdating = False '关闭屏幕更新

    atime = Timer '设atime为正前时间

    Set adoc = Documents.Add

    Set atable = adoc.Tables.Add(Selection.Range, 100, 10)

    With atable.Range

        For i = 1 To 1000

            .Cells(i).Range.Text = i

        Next

    End With

    Application.ScreenUpdating = True

        MsgBox "先放到文档的运行时间为:" & Timer - atime

        '16.3,15.53,15.35

    End Sub

    '几乎是15倍的差别,谁快谁慢应该大家知道了。还顺带说一话:有的软件在操作Word的表格时,就是用类似于“表格6”的方法,而且也没有用Appplication.ScreenUpdating=true。所以,看上去就像在看动画片。(例如:“青山预算之星”的“输出到Word”就是)看来,国产软件还需努力。

    7.         有些尤意末尽的,大家慢慢在程序中体会。

    8.         特别强调:微软本身的许多功能,不是一般的VBA的程序,比VBA要快很多,不明白内部是用什么语言或什么原理在工作。例如:a.邮件合并,速度奇快。如果你试着用VBA来做,速度奇慢。b.修订功能;c.工具、宏、命令listcommands的运行速度。
    等等,都是我们VBA一族所不能及的。(不过来,如果我们的VBA与微软快,微软不PK我们才怪。呵呵,阿Q精神一下,找一下心理平衡。

    展开全文
  • java IText 导出word表格

    千次阅读 2017-11-20 14:20:49
    之前写的导出表格都是导出成Excel,但是接到的项目新需求要求导出成横表类型Word文本,据了解,POI对word的导出支持略低,之前用的是自己设置本地模板,然后读取到模板后进行数值的获取补充,但是由于博主接到的需求所有...

    之前写的导出表格都是导出成Excel,但是接到的项目新需求要求导出成横表类型的Word文本,据了解,POI对word的导出支持略低,之前用的是自己设置本地模板,然后读取到模板后进行数值的获取补充,但是由于博主接到的需求所有的数据都是动态的,所以不可能画个模板然后读取模板导出,因此找到了对导出表单支持良好的iText进行导出,实验效果绝佳,先参考一篇博客写了个小demo,看到导出的是表格,而且是很easy的表格,所以加上自己需要的数据以及代码逻辑就搞定了.废话不多说,首先来看一个简单的IText导出建议表格demo.
    首先说下jar包依赖:

      <dependency>
                <groupId>com.lowagie</groupId>
                <artifactId>itext</artifactId>
                <version>2.1.7</version>
            </dependency>
            <dependency>
                <groupId>com.lowagie</groupId>
                <artifactId>iTextAsian</artifactId>
                <version>2.1.7</version>
            </dependency>
    
            <dependency>
                <groupId>com.lowagie</groupId>
                <artifactId>itext-rtf</artifactId>
                <version>2.1.7</version>
            </dependency>

    然后是网上的小demo:

    package com.rye.test;    
    import java.awt.Color;    
    import java.io.FileNotFoundException;    
    import java.io.FileOutputStream;    
    import java.io.IOException;    
    
    import com.lowagie.text.Cell;    
    import com.lowagie.text.Document;    
    import com.lowagie.text.DocumentException;    
    import com.lowagie.text.Font;    
    import com.lowagie.text.PageSize;    
    import com.lowagie.text.Paragraph;    
    import com.lowagie.text.Table;    
    import com.lowagie.text.rtf.RtfWriter2;    
    /**   
      * 创建word文档 步骤:    
      * 1,建立文档    
      * 2,创建一个书写器    
      * 3,打开文档    
      * 4,向文档中写入数据    
      * 5,关闭文档   
      */   
     public class WordDemo {    
    
      public WordDemo() {    
      }    
    
      /**   
       * @param args   
       */   
      public static void main(String[] args) {    
     // 创建word文档,并设置纸张的大小  
       Document document = new Document(PageSize.A4);   
       try {    
        RtfWriter2.getInstance(document,  
     new FileOutputStream("E:/word.doc"));    
    
        document.open();    
    
       //设置合同头    
    
       Paragraph ph = new Paragraph();    
       Font f  = new Font();    
    
       Paragraph p = new Paragraph("出口合同",   
     new Font(Font.NORMAL, 18, Font.BOLDITALIC, new Color(0, 0, 0)) );    
        p.setAlignment(1);    
        document.add(p);    
        ph.setFont(f);    
    
        // 设置中文字体    
        // BaseFont bfFont =    
        // BaseFont.createFont("STSongStd-Light",  
     "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);    
        // Font chinaFont = new Font();    
        /*   
         * 创建有三列的表格   
         */   
        Table table = new Table(4);    
        document.add(new Paragraph("生成表格"));    
        table.setBorderWidth(1);    
        table.setBorderColor(Color.BLACK);    
        table.setPadding(0);    
        table.setSpacing(0);    
    
        /*   
         * 添加表头的元素   
         */   
        Cell cell = new Cell("表头");//单元格    
        cell.setHeader(true);    
        cell.setColspan(3);//设置表格为三列    
        cell.setRowspan(3);//设置表格为三行    
        table.addCell(cell);    
        table.endHeaders();// 表头结束    
    
        // 表格的主体    
        cell = new Cell("Example cell 2");    
        cell.setRowspan(2);//当前单元格占两行,纵向跨度    
        table.addCell(cell);    
        table.addCell("1,1");    
        table.addCell("1,2");    
        table.addCell("1,3");    
        table.addCell("1,4");    
        table.addCell("1,5");    
        table.addCell(new Paragraph("用java生成的表格1"));    
        table.addCell(new Paragraph("用java生成的表格2"));    
        table.addCell(new Paragraph("用java生成的表格3"));    
        table.addCell(new Paragraph("用java生成的表格4"));    
        document.add(new Paragraph("用java生成word文件"));    
        document.add(table);    
        document.close();    
       } catch (FileNotFoundException e) {    
        e.printStackTrace();    
       } catch (DocumentException e) {    
        e.printStackTrace();    
       } catch (IOException e) {    
        e.printStackTrace();    
       }    
      }    
    
     }  

    来自:http://blog.csdn.net/liuxiao723846/article/details/38613411
    下面是博主自己写的:其中的参数String[] ids,是不同人员的id的数组,String[] field是传入的需要导出的人员熟性,那么接下来—-上代码:

    public  void createTable(String[] ids,String[] field,HttpServletResponse response,HttpServletRequest request) {
            // 创建word文档,并设置纸张的大小
            Document document;
            if (field.length<20){
                document = new Document(PageSize.A4);
            }else {
                document = new Document(PageSize.A2);
            }
    
            try {
                Date date = new Date();
             /*   try {
                    RtfWriter2.getInstance(document,
                            new FileOutputStream("E:/员工花名册.doc"));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }*/
                //测试改变路径
                String fileName = "员工花名册"+".doc";
                ServletOutputStream output = response.getOutputStream();
                response.reset();
                response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "iso-8859-1"));
                RtfWriter2.getInstance(document,
                        output);
                document.open();
    
                //设置合同头
    
                Paragraph ph = new Paragraph();
                Font f  = new Font();
    
                Paragraph p = new Paragraph("员工基本信息",
                        new Font(Font.NORMAL, 18, Font.BOLD, new Color(0, 0, 0)) );
                p.setAlignment(1);
                try {
                    document.add(p);
                } catch (DocumentException e) {
                    e.printStackTrace();
                }
                ph.setFont(f);
    
                // 设置中文字体
                // BaseFont bfFont =
                // BaseFont.createFont("STSongStd-Light",
                //"UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                // Font chinaFont = new Font();
        /*
         * 创建有三列的表格
         */
                Table table = new Table(field.length);
                //table.setWidth((field.length-1)*2);
                /*document.add(new Paragraph("员工信息"));*/
                table.setBorderWidth(1);
                table.setBorderColor(Color.BLACK);
                table.setPadding(0);
                table.setSpacing(0);
    
    
                //准备数据
                List<PersonInfoEntity> personInfoEntityList = new ArrayList<>();
                //Arrays.sort(charArray);
                Map<String,Object> map = new HashMap<>();
                for(int h = 0 ; h<ids.length;h++){
                    map.put("id", ids[ids.length-h-1]);
                    PersonInfoEntity personInfoEntity = personInfoService.selectByMap(map);
                    personInfoEntityList.add(personInfoEntity);
                }
                for (int b = 0;b<=personInfoEntityList.size();b++){
                  /*  可以自定义table的列宽
                  Table table2 = new Table(field.length-1);
                    table2.setWidth((field.length-1)*2);
                    添加图片信息
                *//*document.add(new Paragraph("员工信息"));*//*
                    table2.setBorderWidth(1);
                    table2.setBorderColor(Color.BLACK);
                    table2.setPadding(0);
                    table2.setSpacing(0);*/
                        //设置第一行,也就是表头,不涉及行合并列合并,具体的行合并列合并也可以使用rowSpan之类的进行实现
                    if (b==0){
                        for (int a =0;a<field.length;a++){
                            //table.addCell(field[a]);
                            if (field[a].equals("fullName")||field[a]=="fullName"){
                                table.addCell("姓名");
                            }else if (field[a].equals("beforeName")||field[a]=="beforeName"){
                                table.addCell("曾用名");
                            }else if (field[a].equals("idNumber")||field[a]=="idNumber"){
                                table.addCell("身份证号");
                            }else if (field[a].equals("birthplace")||field[a]=="birthplace"){
                                table.addCell("生日");
                            }else if (field[a].equals("age")||field[a]=="age"){
                                table.addCell("年龄");
                            }else if (field[a].equals("jobNo")||field[a]=="jobNo"){
                                table.addCell("工号");
                            }else if (field[a].equals("deptId")||field[a]=="deptId"){
                                table.addCell("部门");
                            }else if (field[a].equals("nation")||field[a]=="nation"){
                                table.addCell("民族");
                            }else{
                            //由于条目过多:50个字段,所以此处进行省略
                                table.addCell("暂无title");
                            }
                        }
                    }
                    else {
            //设置接下来的数据,也就是真实的人员信息数据           
                                for (int a = 0;a<field.length;a++){
                                    //row1.setRowStyle(style);
                                    if (field[a].equals("fullName")||field[a]=="fullName"){
                                        table.addCell(personInfoEntityList.get(b-1).getFullName());
                                    }else if (field[a].equals("beforeName")||field[a]=="beforeName"){
                                        table.addCell(personInfoEntityList.get(b-1).getBeforeName()==null?"":personInfoEntityList.get(b-1).getBeforeName());
                                    }else if (field[a].equals("idNumber")||field[a]=="idNumber"){
                                        table.addCell(personInfoEntityList.get(b-1).getIdNumber()==null?"":personInfoEntityList.get(b-1).getIdNumber());
                                    }else if (field[a].equals("birthplace")||field[a]=="birthplace"){
                                        table.addCell(personInfoEntityList.get(b-1).getBirthday()==null?"":personInfoEntityList.get(b-1).getBirthday());
                                    }else if (field[a].equals("age")||field[a]=="age"){
                                        String age = "";
                                        if (personInfoEntityList.get(b-1).getAge()!=null){
                                            age = personInfoEntityList.get(b-1).getAge().toString();
                                        }
                                        table.addCell(age);
                                    }else if (field[a].equals("jobNo")||field[a]=="jobNo"){
                                        table.addCell(personInfoEntityList.get(b-1).getJobNo()==null?"":personInfoEntityList.get(b-1).getJobNo());
                                    }else if (field[a].equals("deptId")||field[a]=="deptId"){
                                        String deptName = "";
                 ...
                                        table.addCell(deptName);
                                    }else if (field[a].equals("nation")||field[a]=="nation"){
                                        //枚举
                                        String nationName = "";
                                        if (personInfoEntityList.get(b-1).getNation()!=null&&personInfoEntityList.get(b-1).getNation()!=""){
                                            nationName = NationEnum.getName(Integer.parseInt(personInfoEntityList.get(b-1).getNation()));
                                        }
                                        table.addCell(nationName);
                                   }else{
                            //由于条目过多:50个字段,所以此处进行省略
                                table.addCell("暂无title");
                            }
                                }
    
                        }
                    }
    
               /* document.add(new Paragraph("用java生成word文件"));
                document.add(new Paragraph("这是我自己添加的一行,我最后要的是动态的"));*/
                document.add(table);
                document.close();
            } catch (DocumentException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    值得一提的是:

    String fileName = "员工花名册"+".doc";
                ServletOutputStream output = response.getOutputStream();
                response.reset();
                response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "iso-8859-1"));
                RtfWriter2.getInstance(document,
                        output);
                document.open();

    RtfWriter2.getInstance(document,
    output);getInstance中的流可以是fileOutPutstream也可以是ServletOurPutPtream,开始时想着可不可以转换下,未果,所以看下源码:

     public static RtfWriter2 getInstance(Document var0, OutputStream var1) {
            return new RtfWriter2(var0, var1);
        }

    发现想着转换着实可笑,也是跳进demo的框子里了.

    展开全文
  • word表格换页自动续前表的方法

    千次阅读 2019-09-27 23:37:23
    工作中经常会遇到word表格换页的情况,而经常是需要有续前表的要求的,如果是不经常改动或比较简单的表,可以在整片文档内容及格式调整完成后,从头到尾依次通过以下两比较“笨“”的办法实现: 在分页后表格中...

    工作中经常会遇到word表格换页的情况,而经常是需要有续前表的要求的,如果是不经常改动或比较简单的表,可以在整片文档内容及格式调整完成后,从头到尾依次通过以下两种比较“笨“”的办法实现:

    1. 在分页后表格中第一行填写相应内容,并设置边框为透明

    2. 通过ctrl+shift+enter拆分表格后,在后一页表格填写相应“续表”内容

    以上两种办法的弊端在于,只要表格调整或表格前后调整都需要重新设置,比较繁琐。

    所以下面介绍一种还算比较灵活的办法:通过页眉来实现

    1. 设置重复标题行

    • 选中表格标题行,在“布局”选择重复标题行
    • 这样,会看到分页后的表格依然有标题行

     

    2. 在表格首页和尾页分别插入类型为“连续”的“分节符”

    • 将光标定位后,选择“页面布局”-->“分隔符”-->“连续分节符”,分节后,作为单独的一部分不会影响其他页页眉页脚的内容
    • 如果要看到分节符,可以通过查看草稿或者在选项中的显示中设置为“显示所有格式标记”

    3.插入或编辑页眉

    • 双击要添加续表页的页眉,取消“链接到上一节页眉” 
    • 插入文本框,并输入内容,调整位置,设置文本框格式为“悬浮于文字上方”,如果影响到页边距,尤其是上边距,则再调整下上边距即可

    4. 结束语:

                  优点:这样设置后,再新增或删除部分列表内容后,不需要重复调整。

                  缺点:删除内容很多后,需要撤销续表的页眉

     

     

    展开全文
  • 表格内容水平居中 完整方法(仅参考) 不逼逼多余的废话,简单的讲讲遇到的问题,以及处理办法。 问题描述 调用createRow() 方法创建一行无样式。 调用addRow() 方法创建一行后成功添加边框,但添加的数据都挤...
  • C# 操作现有Word表格详细教程

    万次阅读 2017-10-31 22:46:12
    Word中,我们可以给一个表格设置格式(如设置样式,设置边框,设置单元格背景色),添加或删除行、列,设置行高和列宽,合并和拆分单元格,插入图片等,也可以删除该表格。在这篇文章中我将介绍如何使用C#来实现...
  • 如何用python提取Word 表格内容

    千次阅读 2019-11-14 09:39:26
    一个公司有若干个部门,一个部门有若干个员工,每一个员工填写一份自己的信息(Word 版),然后录入系统,Word 表格模板如下:  提取 Word 表格内容  填入内容如: 提取 Word 表格内容 首先我们需要做的就是把...
  • Word表格处理技巧

    千次阅读 2009-05-05 05:56:00
    1 文字巧妙转换成表格通常大家制作表格时,都是采用表格和边框工具...具体方法是这样:(1)按照自己的计划先将表格中的各项内容输入Word文件中,在这需注意的是,一定要利用一特别的分隔符隔开准备产生表格列线的文
  • VC++中调用word进行word表格的填写

    千次阅读 2011-05-15 23:29:00
    VC++中调用word进行word表格的填写  <br /> 下载源代码 在实际应用中,编程者往往喜欢程序能自动生成word说明文档,说明程序运行的状况或运行的结果;或者程序能提取数据库的内容生成word表格,使用户能...
  • c#操作word表格

    千次阅读 2007-05-15 12:31:00
    主要是对word表格的操作,以下是部分代码,关于操作不规则表格的.using System;using System.Collections;using System.ComponentModel;using System.Data;using System.Drawing;using System.Web;using System.
  • 数据插入到word表格中并导出word模板,下载word模板。
  • word2016 表格标尺偏移问题

    千次阅读 2019-04-02 15:49:27
     搜了好多帖子都没找到原因,胡扯的倒是挺多的,最后凭借自己多年word经验,终于搞定了,原来是不小心动了表格的文字环绕类型,只需要在表格属性里面改一下就好了——  问题解决,表格的标尺又对齐了,没想到...
  • 使用VBA操作Word表格

    万次阅读 2015-05-27 11:12:53
    一、生成表格 Private Sub CreateTable(mRows As Integer, mColumns)  Dim mRange As Range  Set mRange = ActiveDocument.Range  mRange.SetRange Start:=ActiveDocument.Range.End, End:=ActiveDocument.Ra
  • poi 操作word表格,如设置表格宽度、行高、表格样式等。 1.表格或单元格宽度: 默认TblW的type属性为STTblWidth.AUTO,即自动伸缩。所以要调整为指定类型:STTblWidth.DXA 1)表格宽: CTTblPr tblPr = xtab2....
  • python-docx编辑word表格

    千次阅读 2019-02-15 17:00:00
    一、修改数据类型(中英) 需求: 代码: #-*-coding:gbk*- import os import docx #from docx.enum.table import WD_TABLE_ALIGNMENT from docx.enum.text import WD_ALIGN_PARAGRAPH filepath = r'f...
  • 读取excel内容,写入word表格

    万次阅读 2018-12-27 15:56:52
    row.add(count+"."+list.get(3)+"("+list.get(4)+"h,一般)"); row.add(total+"h"); rows.add(row); } exportWord(rows, "2018年10月工作总结"); } //导出word public static void exportWord...
  • poi导出word表格的问题

    千次阅读 2015-05-08 09:15:06
    poi导出word的问题
  •  表格是一简明、概要的表意方式。其结构严谨,效果直观,特 别是在制作网页的时候,表格的使用是非常普遍的,因为它可以方 便地实现图文混排。利用Word 2000 编制网页是初级用户的首选, 所以学会使用表格是一项...
  • web环境;spring+jQuery+hibernate  ... 下载word版 $("#dwywjs").click(function(){  var attid="${映射表文件名.attid}";//对应数据库里该表名里的某一行具体的数据  var userid="${userSess
  • C# 生成数据字典(Word 表格)

    千次阅读 2014-03-11 14:51:15
    先看一张效果图: 如果您觉得您会了,请绕道... ...Microsoft.Office.Interop.Word 第二步:说说精髓部分,并非完整代码,末尾 附上 源码 Microsoft.Office.Interop.Word.Application app = null;
  • Word模型对象Document、Range、Selection中都含有一个Tables的属性,它是表格的集合,我们想要向一个word文档中添加表格,就需要调用上述三个对象的Tables属性的Add方法,将表格添加进去。  我们先来看下,Add方法...
  • 读取Word文档的各种复杂表格内容

    千次阅读 2018-04-14 15:30:21
    表头也有业务意思(2)一行为一个业务数据,可能会跨行(3)列可能会有跨列、跨行(4)单元格中图片、数学公式、嵌套表格、文件等比如,以下表格具体代码:https://github.com/suncht/wordtable-...
  • 使用word中的宏实现选中指定类型表格,并且批量修改样式结构
  • 使用VC 6.0实现自动生成Word表格

    千次阅读 热门讨论 2008-11-30 10:07:00
    最近在VC6.0 环境下开发一个管理系统,界面开发花了不少功夫却感觉华而不实,自动生成word表格的功能实现了,倒觉得蛮实用的(1)首先,向你的项目中添加word 2003类型库:方法是在MFC ClassWizard中点击按钮Add ...
  • 用DELPHI怎样生成WORD表格文档

    千次阅读 2012-12-19 09:43:16
    Delphi与Word之间的融合技术   一、VBA代码含义   Microsoft Word是一个集成化环境,是美国微软公司的字处理系统,但是它决不仅仅是一个字处   理系统,它集成了Microsoft Visual Basic,可以通过编程来...
  • SQL Server表结构转为word表格

    千次阅读 2009-01-10 20:57:00
    最近摸索出一套SQL Server表结构转为word表格的方法,希望能给大家带来点帮助。1.打开SQL Server企业管理器,打开需要写文档的数据库2.打开SQL查询分析器,输入以下代码: SELECT 表名=case when a.colorder=1 ...
  • JAVA根据数据库表生成word表格文档

    千次阅读 2017-06-08 13:22:58
    public static String[] FILEDS = new String[]{"字段名","字段类型","长度","主键/外键","默认值","描述"}; public static int[] COLUMN_WIDTHS = new int[] {1504,1504,1504,1504,1504,1504}; public static ...
  • Word 表格换页自动“续表”方法

    万次阅读 2015-10-23 11:58:21
    最近写毕业论文,排版的时候把 Word 上的十八般武艺基本都把玩了一遍,唯独换页“续表”这个搞不定。利用常规方法,标题行换页重复是可以做到的,本来想到把“续表”也作为一个标题行,然后第一页上的“续表”用白色...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 44,331
精华内容 17,732
关键字:

word表格3种类型