word 文本、动态表格填充

package cn.rao.WordUtils;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.BodyElementType;
import org.apache.poi.xwpf.usermodel.IBodyElement;
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.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTInd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSpacing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;


public class WordUtils {

	/**
	 * 循环填充表格内容
	 * @param xwpfDocument
	 * @param params
	 * @param tableIndex
	 * @throws Exception
	 */
	public static void insertValueToTable(XWPFTable table, Integer startRow, List<Map<String,String>> params, Boolean deleteTemplateCol) throws Exception {
		try {

			List<XWPFTableRow> rows = table.getRows();
			if (rows.size() < 2) {
				throw new Exception("tableIndex对应表格应该为2行");
			}
			// 模板的那一行
			XWPFTableRow tmpRow = rows.get(startRow-1);
			List<XWPFTableCell> tmpCells = null;
			List<XWPFTableCell> cells = null;
			XWPFTableCell tmpCell = null;
			tmpCells = tmpRow.getTableCells();

			String cellText = null;
			for (int i = 0, len = params.size(); i < len; i++) {

				Map<String, String> map = params.get(i);
				// 创建新的一行
				XWPFTableRow row = table.insertNewTableRow(i+startRow);
				row.getCtRow().setTrPr(tmpRow.getCtRow().getTrPr());
				// 获取模板的行高 设置为新一行的行高
				row.setHeight(tmpRow.getHeight());
				addTemplateRow(tmpRow, row);
				cells = row.getTableCells();
				for (int k = 0, klen = cells.size(); k < klen; k++) {
					tmpCell = tmpCells.get(k);
					XWPFTableCell cell = cells.get(k);
					cellText = tmpCell.getText();
					if (StringUtils.isNotBlank(cellText)) {
						//转换为mapkey对应的字段
						if (map.containsKey(cellText)) {
							//							copyCellAndSetValue(tmpCell, cell, map.get(cellText));
							setCellText(tmpCell, cell, map.get(cellText));
						}
					}
				}
			}
			// 删除模版行
			if(deleteTemplateCol) {
				table.removeRow(1);
			}
		}catch (Exception e){
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * 
	 * @Title: insertValueToTable
	 * @Description: 向表格中写入数据
	 * @param table
	 * @param rowNum
	 * @param params
	 * @throws Exception    参数描述
	 * @return void  返回类型
	 * @throws
	 */
	public static void insertValueToTable(XWPFTable table, Integer rowNum, Map<String,String> params) throws Exception{

		try {
			List<XWPFTableRow> rows = table.getRows();
			XWPFTableRow row = rows.get(rowNum-1);
			List<XWPFTableCell> cells = row.getTableCells();
			for (int k = 0, klen = cells.size(); k < klen; k++) {

				XWPFTableCell cell = cells.get(k);
				String cellText = cell.getText();
				if (StringUtils.isNotBlank(cellText)) {
					//转换为mapkey对应的字段
					if (params.containsKey(cellText)) {

						XWPFParagraph cellP = cell.getParagraphs().get(0);
						XWPFRun cellR = cellP.getRuns().get(0);
						cellR.setText(params.get(cellText), 0);
					}
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * 添加模板行
	 * @param sourceRow
	 * @param targetRow
	 */
	private static void addTemplateRow(XWPFTableRow sourceRow, XWPFTableRow targetRow) {


		List<XWPFTableCell> cellList = sourceRow.getTableCells();
		if (null == cellList) {
			return;
		}

		XWPFTableCell targetCell = null;
		for (XWPFTableCell sourceCell : cellList) {
			targetCell = targetRow.addNewTableCell();
			//列属性
			targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
			//段落属性
			if(sourceCell.getParagraphs()!=null&&sourceCell.getParagraphs().size()>0){                     
				targetCell.getParagraphs().get(0).getCTP().setPPr(sourceCell.getParagraphs().get(0).getCTP().getPPr());
				if(sourceCell.getParagraphs().get(0).getRuns()!=null&&sourceCell.getParagraphs().get(0).getRuns().size()>0){
					XWPFRun cellR = targetCell.getParagraphs().get(0).createRun();
					cellR.setText(sourceCell.getText());
					cellR.setBold(sourceCell.getParagraphs().get(0).getRuns().get(0).isBold());
				}else{
					targetCell.setText(sourceCell.getText());
				}
			}else{
				targetCell.setText(sourceCell.getText());
			}
		}
	}
	
	/**
	 *  复制模板行的属性并填充单元格
	 * @param tmpCell
	 * @param cell
	 * @param text
	 * @throws Exception
	 */
	private static void setCellText(XWPFTableCell tmpCell, XWPFTableCell cell,String text) {

		cell.setColor(tmpCell.getColor());


		CTTc cttc2 = tmpCell.getCTTc();
		CTTcPr ctPr2 = cttc2.getTcPr();
		CTTc cttc = cell.getCTTc();
		CTTcPr ctPr = cttc.addNewTcPr();
		if (ctPr2.getTcW() != null) {
			ctPr.addNewTcW().setW(ctPr2.getTcW().getW());
		}
		if (ctPr2.getVAlign() != null) {
			ctPr.addNewVAlign().setVal(ctPr2.getVAlign().getVal());
		}


		if (ctPr2.getTcBorders() != null) {
			ctPr.setTcBorders(ctPr2.getTcBorders());
		}
		XWPFParagraph tmpParagraph = tmpCell.getParagraphs().get(0);
		XWPFParagraph cellP = cell.getParagraphs().get(0);
		// 模板行
		XWPFRun tmpRun = null;
		if (tmpParagraph.getRuns() != null && tmpParagraph.getRuns().size() > 0) {
			// 第一行为模板行
			tmpRun = tmpParagraph.getRuns().get(0);
		}

		// 写入数据
		//        XWPFRun cellR = cellP.createRun();
		//        cellR.setText(text);
		XWPFRun cellR = null;
		if(cellP.getRuns().size() == 0) {
			cellR = cellP.createRun();
		}else {
			cellR = cellP.getRuns().get(0);
		}
		cellR.setText(text, 0);


		// 复制字体信息
		if (tmpRun != null) {
			if(!cellR.isBold()){
				cellR.setBold(tmpRun.isBold());
			}
			cellR.setItalic(tmpRun.isItalic());
			cellR.setUnderline(tmpRun.getUnderline());
			cellR.setColor(tmpRun.getColor());
			cellR.setTextPosition(tmpRun.getTextPosition());
			if (tmpRun.getFontSize() != -1) {
				cellR.setFontSize(tmpRun.getFontSize());
			}
			if (tmpRun.getFontFamily() != null) {
				cellR.setFontFamily(tmpRun.getFontFamily());
			}
			if (tmpRun.getCTR() != null) {
				if (tmpRun.getCTR().isSetRPr()) {

					CTRPr tmpRPr = tmpRun.getCTR().getRPr();

					if (tmpRPr.isSetRFonts()) {

						CTFonts tmpFonts = tmpRPr.getRFonts();
						CTRPr cellRPr = cellR.getCTR().isSetRPr() ? cellR
								.getCTR().getRPr() : cellR.getCTR().addNewRPr();
								CTFonts cellFonts = cellRPr.isSetRFonts() ? cellRPr
										.getRFonts() : cellRPr.addNewRFonts();
										cellFonts.setAscii(tmpFonts.getAscii());
										cellFonts.setAsciiTheme(tmpFonts.getAsciiTheme());
										cellFonts.setCs(tmpFonts.getCs());
										cellFonts.setCstheme(tmpFonts.getCstheme());
										cellFonts.setEastAsia(tmpFonts.getEastAsia());
										cellFonts.setEastAsiaTheme(tmpFonts.getEastAsiaTheme());
										cellFonts.setHAnsi(tmpFonts.getHAnsi());
										cellFonts.setHAnsiTheme(tmpFonts.getHAnsiTheme());
					}
				}
			}
		}
		// 复制段落信息
		cellP.setAlignment(tmpParagraph.getAlignment());
		cellP.setVerticalAlignment(tmpParagraph.getVerticalAlignment());
		cellP.setBorderBetween(tmpParagraph.getBorderBetween());
		cellP.setBorderBottom(tmpParagraph.getBorderBottom());
		cellP.setBorderLeft(tmpParagraph.getBorderLeft());
		cellP.setBorderRight(tmpParagraph.getBorderRight());
		cellP.setBorderTop(tmpParagraph.getBorderTop());
		cellP.setPageBreak(tmpParagraph.isPageBreak());
		if (tmpParagraph.getCTP() != null) {
			if (tmpParagraph.getCTP().getPPr() != null) {
				CTPPr tmpPPr = tmpParagraph.getCTP().getPPr();
				CTPPr cellPPr = cellP.getCTP().getPPr() != null ? cellP
						.getCTP().getPPr() : cellP.getCTP().addNewPPr();
						// 复制段落间距信息
						CTSpacing tmpSpacing = tmpPPr.getSpacing();
						if (tmpSpacing != null) {
							CTSpacing cellSpacing = cellPPr.getSpacing() != null ? cellPPr
									.getSpacing() : cellPPr.addNewSpacing();
									if (tmpSpacing.getAfter() != null) {
										cellSpacing.setAfter(tmpSpacing.getAfter());
									}
									if (tmpSpacing.getAfterAutospacing() != null) {
										cellSpacing.setAfterAutospacing(tmpSpacing
												.getAfterAutospacing());
									}
									if (tmpSpacing.getAfterLines() != null) {
										cellSpacing.setAfterLines(tmpSpacing.getAfterLines());
									}
									if (tmpSpacing.getBefore() != null) {
										cellSpacing.setBefore(tmpSpacing.getBefore());
									}
									if (tmpSpacing.getBeforeAutospacing() != null) {
										cellSpacing.setBeforeAutospacing(tmpSpacing
												.getBeforeAutospacing());
									}
									if (tmpSpacing.getBeforeLines() != null) {
										cellSpacing.setBeforeLines(tmpSpacing.getBeforeLines());
									}
									if (tmpSpacing.getLine() != null) {
										cellSpacing.setLine(tmpSpacing.getLine());
									}
									if (tmpSpacing.getLineRule() != null) {
										cellSpacing.setLineRule(tmpSpacing.getLineRule());
									}
						}
						// 复制段落缩进信息
						CTInd tmpInd = tmpPPr.getInd();
						if (tmpInd != null) {
							CTInd cellInd = cellPPr.getInd() != null ? cellPPr.getInd()
									: cellPPr.addNewInd();
							if (tmpInd.getFirstLine() != null) {
								cellInd.setFirstLine(tmpInd.getFirstLine());
							}
							if (tmpInd.getFirstLineChars() != null) {
								cellInd.setFirstLineChars(tmpInd.getFirstLineChars());
							}
							if (tmpInd.getHanging() != null) {
								cellInd.setHanging(tmpInd.getHanging());
							}
							if (tmpInd.getHangingChars() != null) {
								cellInd.setHangingChars(tmpInd.getHangingChars());
							}
							if (tmpInd.getLeft() != null) {
								cellInd.setLeft(tmpInd.getLeft());
							}
							if (tmpInd.getLeftChars() != null) {
								cellInd.setLeftChars(tmpInd.getLeftChars());
							}
							if (tmpInd.getRight() != null) {
								cellInd.setRight(tmpInd.getRight());
							}
							if (tmpInd.getRightChars() != null) {
								cellInd.setRightChars(tmpInd.getRightChars());
							}
						}
			}
		}
	}
	
	
	/**
	 * 合并行
	 * @param table
	 * @param col
	 * @param fromRow
	 * @param toRow
	 */
	public static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {
		for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
			CTVMerge vmerge = CTVMerge.Factory.newInstance();
			if (rowIndex == fromRow) {
				vmerge.setVal(STMerge.RESTART);
			} else {
				vmerge.setVal(STMerge.CONTINUE);
			}
			XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
			CTTcPr tcPr = cell.getCTTc().getTcPr();
			if (tcPr != null) {
				tcPr.setVMerge(vmerge);
			} else {
				tcPr = CTTcPr.Factory.newInstance();
				tcPr.setVMerge(vmerge);
				cell.getCTTc().setTcPr(tcPr);
			}
		}
	}
	/**
	 * 合并列
	 *
	 * @param table
	 * @param row
	 * @param fromCell
	 * @param toCell
	 */
	public static 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);
				//cellCount为表格总列数
				int cellCount = table.getRow(row).getTableCells().size();
				Integer width = (toCell - fromCell + 1) / cellCount * table.getCTTbl().getTblPr().getTblW().getW().intValue();
				cell.getCTTc().getTcPr().addNewTcW().setW(BigInteger.valueOf(width));
			} else {
				// Cells which join (merge) the first one, are set with CONTINUE
				cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
			}
		}
	}
	
	
	/**
	 * 
	 * @Title: insertTemplateTableCol
	 * @Description: 添加表格列
	 * @param table  表格
	 * @param datas    待写入数据
	 * @return void  返回类型
	 * @throws
	 */
	public static void insertTemplateTableCol(XWPFTable table, List<List<String>> datas) {

		List<XWPFTableRow> rows = table.getRows();
		for(int k = 0; k < datas.size(); k++) {

			List<String> data = datas.get(k);
			for(int i = 0; i < rows.size(); i++) {

				XWPFTableRow row = rows.get(i);
				XWPFTableCell tmpCell =  row.getCell(0);
				XWPFTableCell cell = row.createCell();
				setCellText(tmpCell, cell, data.get(i));
			}
		}
	}
	
	/**
	 * 替换文本中的占位符
	 * @param document
	 * @param textMap
	 */
	public static void changeText(XWPFDocument document, Map<String, Object> textMap) {
		// 获取段落集合
		// 返回包含页眉或页脚文本的段落
		List<XWPFParagraph> paragraphs = document.getParagraphs();
		// 增强型for循环语句,前面一个为声明语句,后一个为表达式
		for (XWPFParagraph paragraph : paragraphs) {
			// 判断此段落是否需要替换
			String text = paragraph.getText();// 检索文档中的所有文本
			if (checkText(text)) {
				List<XWPFRun> runs = paragraph.getRuns();
				for (XWPFRun run : runs) {
					// 替换模板原来位置
					Object ob = changeValue(run.toString(), textMap);
					if (ob instanceof String) {
						if (textMap.containsKey(run.toString())) {

							run.setText((String) ob, 0);
						}
					}
				}
			}
		}
	}
	
	
	/**
	 * 
	 * @Title: insertTemplatePlaceholder
	 * @Description: 插入模板表占位符
	 * @param key    参数描述
	 * @return void  返回类型
	 * @throws
	 */
	public static void insertTemplatePlaceholder(XWPFDocument document,String key, List<List<String>> placeholderList) {


		List<XWPFParagraph> paragraphList = document.getParagraphs();
		if (paragraphList != null && paragraphList.size() > 0) {

			XWPFParagraph paragraphUse = null;
			for (XWPFParagraph paragraph : paragraphList) {

				List<XWPFRun> runs = paragraph.getRuns();
				for (int i = 0; i < runs.size(); i++) {
					String text = runs.toString();

					if (text.indexOf(key) >= 0) {
						paragraphUse = paragraph;
						break;
					}
				}
			}

			// 在list中增加paragraph 会报异常
			// 在cursor位置插入段落,按顺序添加
			for(int i=0; i< placeholderList.size(); i++) {

				List<String> placeholders = placeholderList.get(i);
				for(String placeholder :placeholders) {
					paragraphUse = insertPlaceholder(document, paragraphUse, placeholder);
				}
			}

		}
	}

	/**
	 * 
	 * @Title: insertPlaceholder
	 * @Description: 插入占位符
	 * @param document
	 * @param sourceParagraph
	 * @param key
	 * @return    参数描述
	 * @return XWPFParagraph  返回类型
	 * @throws
	 */
	private static XWPFParagraph insertPlaceholder(XWPFDocument document, XWPFParagraph sourceParagraph, String key) {

		XmlCursor cursor = sourceParagraph.getCTP().newCursor();
		cursor.toNextSibling();
		XWPFParagraph paragraphNew = document.insertNewParagraph(cursor);
		paragraphNew.getCTP().setPPr(sourceParagraph.getCTP().getPPr());
		XWPFRun xwpfRun = paragraphNew.createRun();
		xwpfRun.setText(key);
		xwpfRun.setFontSize(sourceParagraph.getRuns().get(0).getFontSize());
		xwpfRun.setFontFamily(sourceParagraph.getRuns().get(0).getFontFamily());

		return paragraphNew;
	}
	
	/* 检查文本中是否包含指定的字符(此处为“$”),并返回值 */
	private static boolean checkText(String text) {
		boolean check = false;
		if (text.contains("$")) {
			check = true;
		}
		return check;
	}

	private static Object changeValue(String value, Map<String, Object> textMap) {
		Set<Map.Entry<String, Object>> textSets = textMap.entrySet();
		Object valu = "";
		for (Map.Entry<String, Object> textSet : textSets) {
			// 匹配模板与替换值 格式${key}
			String key = textSet.getKey();
			if (value.contains(key)) {
				valu = textSet.getValue();
			}
		}
		return valu;
	}
	
	
	/**
	 * 
	 * @Title: reOpen
	 * @Description: 重新打开文档
	 * @param document
	 * @param destPath
	 * @return
	 * @throws Exception    参数描述
	 * @return XWPFDocument  返回类型
	 * @throws
	 */
	public static XWPFDocument reOpen(XWPFDocument document, String destPath)throws Exception {

		try {
			FileOutputStream out = new FileOutputStream(destPath);
			document.write(out);

			FileInputStream is = new FileInputStream(destPath);
			XWPFDocument newDocument = new XWPFDocument(is);
			
			return newDocument;
		} catch (IOException e) {
			throw new Exception(" 重新打开模板文档失败:", e);
		}
	}

	
	/**
	 * 
	 * @Title: formTime
	 * @Description: 获取格式化时间字符串
	 * @param type
	 * @return    参数描述
	 * @return String  返回类型
	 * @throws
	 */
	public static String formTime(String type) {

		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(type);
		Date date = new Date(System.currentTimeMillis());
		String timeString = simpleDateFormat.format(date);

		return timeString;
	}
	
	
	/**
	 * 删除占位符或占位符所在的段落
	 * @param document
	 * @param placeholders
	 * @param deleteParagraph 是否删除段落
	 */
	public static void deletePlaceholder(XWPFDocument document, List<String> placeholders, Boolean deleteParagraph) {
		// 获取段落集合
		// 返回包含页眉或页脚文本的段落
		List<XWPFParagraph> paragraphs = document.getParagraphs();

		List<IBodyElement> listBe = document.getBodyElements();
		List<Integer> runList = new ArrayList<>();
		int n = 0;
		for(int i = 0; i < listBe.size(); i++){

			XWPFParagraph paragraph = paragraphs.get(n);

			List<XWPFRun> runs = paragraph.getRuns();
			for (XWPFRun run : runs) {
				// 替换模板原来位置
				String runText = run.toString();
				if(placeholders.contains(runText)) {
					if(!deleteParagraph) {
						run.setText("", 0);
					}else {
						runList.add(i);
					}
				}
			}

			n++;
			//非文字段落n-1
			if(listBe.get(i).getElementType() != BodyElementType.PARAGRAPH){
				n--;
			}
		}

		//遍历list删除
		for(int i = runList.size() - 1; i >= 0; i--){
			document.removeBodyElement(runList.get(i));
		}
	}
	
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>cn.rao</groupId>
	<artifactId>WordUtils</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>WordUtils</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.16</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.16</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.commons/org.apache.commons.lang -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.10</version>
		</dependency>
		<dependency>
			<groupId>org.apache.pdfbox</groupId>
			<artifactId>pdfbox</artifactId>
			<version>2.0.12</version>
		</dependency>
		<dependency>
			<groupId>com.aspose</groupId>
			<artifactId>aspose-words</artifactId>
			<version>15.8.0</version>
			<scope>system</scope>
			<systemPath>${project.basedir}/lib/aspose-words-15.8.0-jdk16.jar</systemPath>
		</dependency>
	</dependencies>
</project>

需要注意的点:

1、向word中插入动态表格步骤

        (1)在word模板中定义占位符,该占位符表示表格插入的行

        (2)获取word 模板中需要的模板表,并定义占位符与模板表的对应关系

        (3)遍历word模板,查找模板表的占位符,并替换为模板表

        (4)关闭word文件并重新打开(此步必须

        (5)填写数据

        (6)删除占位符,删除占位符所在的段落

2、向word中插入占位符

        插入占位符时不能直接在word中输入,应该预先在txt文档中输入完整的占位符,如:${addTable},然后将整个占位符字符串拷贝到word中

          

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值