POI与easyExcel的区别:

POI是通过WorkBook来解析表格文件的,虽然内部有使用到Sax模式,能后大大的提高效率,但是要解析大文件(10w,100w行)的话很容易出现OOM(内存溢出)。

相比之下,

1、easyExcel解析实在磁盘上进行的,几乎可以将几mb级别的内存压缩到kb级别,几乎不用担心OOM;

2、用Java模型进行关系映射,项目中最常用的就Java模型映射,通过 @ExcelProperty注解就可以完成行与列的映射;

3、easyExcel中有一个类AnalysisEventListener,里面有一个方法invoke实现了一行一行返回,另外还可以重写该类的doAfterAllAnalysed方法,用来做事后处理之类的操作,相当的灵活。

准备阶段

第一步:引入easyExcel依赖

com.alibaba

easyexcel

1.1.2-beta5

View Code

第二步:自定义监听器ExcelListener继承于AnalysisEventListener,重写invoke()方法(可以读取到excel每一行的数据)和doAfterAllAnalysed()方法(用于后置处理),其中datas用于存取读取到的数据,importHeads为导入表头,modelHeads

packagecom.cloud.data.utils;importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.event.AnalysisEventListener;importcom.alibaba.excel.metadata.ExcelHeadProperty;importorg.springframework.util.StringUtils;importjava.lang.reflect.Field;importjava.util.ArrayList;importjava.util.LinkedHashMap;importjava.util.List;importjava.util.Map;/*** EasyExcell导入监听类*/

public class ExcelListener extendsAnalysisEventListener {//自定义用于暂时存储数据

private List datas = new ArrayList<>();//导入表头

private String importHeads = "";//模版表头

private String modelHeads = "";/*** 通过 AnalysisContext对象获取当前sheet,当前行等数据*/@Overridepublic voidinvoke(Object o, AnalysisContext analysisContext) {

Integer currentRowNum=analysisContext.getCurrentRowNum();//获取导入表头,默认第一行为表头

if(currentRowNum == 0){try{

Map m =objToMap(o);for(Object v : m.values()) {if(!StringUtils.isEmpty(v)){

importHeads+= String.valueOf(v).trim() + ",";

}

}

}catch(Exception e) {

e.printStackTrace();

}

}else{

datas.add(o);

}

}/*** 监听器获取模板表头*/@Overridepublic voiddoAfterAllAnalysed(AnalysisContext analysisContext) {//获取模版表头

ExcelHeadProperty ehp =analysisContext.getExcelHeadProperty();for(Lists : ehp.getHead()){

modelHeads+= s.get(0) + ",";

}

}//Object转换为Map

private Map objToMap(Object obj) throwsException{

Map map = new LinkedHashMap();

Field[] fields=obj.getClass().getDeclaredFields();for(Field field : fields){

field.setAccessible(true);

map.put(field.getName(), field.get(obj));

}returnmap;

}public ListgetDatas() {returndatas;

}public void setDatas(Listdatas) {this.datas =datas;

}publicString getImportHeads() {returnimportHeads;

}public voidsetImportHeads(String importHeads) {this.importHeads =importHeads;

}publicString getModelHeads() {returnmodelHeads;

}public voidsetModelHeads(String modelHeads) {this.modelHeads =modelHeads;

}

}

View Code

第三步:添加接收excel导入的实体类OutDbillDto,继承BaseRowModel,其中@ExcelProperty(value = "机组调度名称(必填)", index = 0)对应excel表头的(value为表头名称,index为索引位置)

packagecom.cloud.data.entity.dto;importcom.alibaba.excel.annotation.ExcelProperty;importcom.alibaba.excel.metadata.BaseRowModel;importlombok.Data;importjava.io.Serializable;/*** 日结算账单导入类*/@Datapublic class OutDbillDto extends BaseRowModel implementsSerializable {

@ExcelProperty(value= "机组调度名称(必填)", index = 0)privateString unitDispatchName;

@ExcelProperty(value= "日期(必填)", index = 1)privateString billTime;

@ExcelProperty(value= "收费项目", index = 2)privateString project;

@ExcelProperty(value= "本期电量(万kWh)", index = 3)privateString quan;

@ExcelProperty(value= "单价(厘/kWh)", index = 4)privateString avgPrice;

@ExcelProperty(value= "本期电费(元)",index = 5)privateString price;/**标记*/

privateString mark;/**错误信息*/

privateString errMsg;

}

View Code

第四步,定义EasyExcelUtil工具类,用于读取excel

packagecom.cloud.data.utils;importcom.alibaba.excel.EasyExcelFactory;importcom.alibaba.excel.ExcelReader;importcom.alibaba.excel.ExcelWriter;importcom.alibaba.excel.metadata.BaseRowModel;importcom.alibaba.excel.metadata.Sheet;importcom.alibaba.excel.support.ExcelTypeEnum;importcom.cloud.frame.util.DateUtil;importcom.google.common.collect.Lists;importorg.apache.commons.beanutils.BeanUtils;importorg.apache.poi.ss.formula.functions.T;importorg.springframework.web.multipart.MultipartFile;import java.io.*;importjava.nio.file.Path;importjava.nio.file.Paths;import java.util.*;public classEasyExcelUtil {/*** 读取某个 sheet 的 Excel

*

*@paramexcel 文件

*@paramrowModel 实体类映射,继承 BaseRowModel 类

*@returnExcel 数据 list*/

public static List readExcel(MultipartFile excel, BaseRowModel rowModel) throwsIOException {return readExcel(excel, rowModel, 1, 1);

}/*** 读取某个 sheet 的 Excel

*@paramexcel 文件

*@paramrowModel 实体类映射,继承 BaseRowModel 类

*@paramsheetNo sheet 的序号 从1开始

*@returnExcel 数据 list*/

public static Map readExcel(MultipartFile excel, BaseRowModel rowModel, int sheetNo) throwsIOException {

Map result = new HashMap<>();

ExcelListener excelListener= newExcelListener();

ExcelReader reader=getReader(excel, excelListener);if(Objects.isNull(reader)) {return null;

}

reader.read(new Sheet(sheetNo, 0, rowModel.getClass()));//校验表头

Boolean flag = false;if(excelListener.getImportHeads().equals(excelListener.getModelHeads())){

flag= true;

}

result.put("flag", flag);

result.put("datas", excelListener.getDatas());returnresult;

}/*** 读取某个 sheet 的 Excel

*@paramexcel 文件

*@paramrowModel 实体类映射,继承 BaseRowModel 类

*@paramsheetNo sheet 的序号 从1开始

*@paramheadLineNum 表头行数,默认为1

*@returnExcel 数据 list*/

public static List readExcel(MultipartFile excel, BaseRowModel rowModel, int sheetNo, int headLineNum) throwsIOException {

ExcelListener excelListener= newExcelListener();

ExcelReader reader=getReader(excel, excelListener);if (reader == null) {return null;

}

reader.read(newSheet(sheetNo, headLineNum, rowModel.getClass()));returnexcelListener.getDatas();

}/*** 读取指定sheetName的Excel(多个 sheet)

*@paramexcel 文件

*@paramrowModel 实体类映射,继承 BaseRowModel 类

*@returnExcel 数据 list

*@throwsIOException*/

public static List readExcel(MultipartFile excel, BaseRowModel rowModel,String sheetName) throwsIOException {

ExcelListener excelListener= newExcelListener();

ExcelReader reader=getReader(excel, excelListener);if (reader == null) {return null;

}for(Sheet sheet : reader.getSheets()) {if (rowModel != null) {

sheet.setClazz(rowModel.getClass());

}//读取指定名称的sheet

if(sheet.getSheetName().contains(sheetName)){

reader.read(sheet);break;

}

}returnexcelListener.getDatas();

}/*** 返回 ExcelReader

*@paramexcel 需要解析的 Excel 文件

*@paramexcelListener new ExcelListener()

*@throwsIOException*/

private static ExcelReader getReader(MultipartFile excel,ExcelListener excelListener) throwsIOException {

String filename=excel.getOriginalFilename();if(Objects.nonNull(filename) && (filename.toLowerCase().endsWith(".xls") || filename.toLowerCase().endsWith(".xlsx"))){

InputStream is= newBufferedInputStream(excel.getInputStream());return new ExcelReader(is, null,null, excelListener, false);

}else{return null;

}

}/*** 将导入失败的数据写到系统指定路径

*@paramfailDatas 数据

*@paramname 文件名

*@paramdir 写入的路径

*@paramheader Excel表头

*@paramcolumns 需要写入Excel的对象属性集合

*@paramindex 合并单元格索引

*@return*@throwsException*/

public static String saveExcel2Loacl(List> failDatas, String name, String dir, String[] header,String[] columns,Integer index) throwsException {//1.唯一文件名

String fileName = name + "_" + DateUtil.getNowStr(DateUtil.TIME_FORMAT)+".xls";

Path path=Paths.get(ExcelUtil.FILE_UPLOAD_ROOT, dir).toAbsolutePath();

File file= newFile(path.toString());if (!file.exists()){

file.mkdirs();

}//2.添加Sheet名

Sheet sheet = new Sheet(1,0);

sheet.setSheetName(name);//3.动态添加Excel表头

List> head = new ArrayList<>();for(String h : header) {

head.add(Lists.newArrayList(h));

}

sheet.setHead(head);//4.写入数据

List datas = newArrayList(failDatas);

List> data = new ArrayList<>();for(Object var : datas) {

List objects =Lists.newArrayList();

Arrays.stream(columns).forEach(e->{try{

String property=BeanUtils.getProperty(var, e);

objects.add(property);

}catch(Exception err) {

err.printStackTrace();

}

});

data.add(objects);

}

FileOutputStream fileOutputStream= newFileOutputStream(Paths.get(ExcelUtil.FILE_UPLOAD_ROOT, dir, fileName).toFile());

ExcelWriter writer= EasyExcelFactory.getWriter(fileOutputStream, ExcelTypeEnum.XLS, true);

writer.write1(data,sheet);//5.合并单元格

for (int i = 1; i <= failDatas.size(); i = i +index) {

writer.merge(i,i+ index - 1,0,0);

writer.merge(i,i+ index - 1,1,1);

writer.merge(i,i+ index - 1,2,2);

}

writer.finish();returnfileName;

}

}

View Code

执行原理:

ExcelAnalyserImpl是解析器的真正实现,整合了v07好人v03,解析的时候会根据getSaxAnalyser来选择使用哪种版本的解析器。

通过ExcelAnalyserImpl()构造方法,将inputstream(也就是file文件)和自定义的监听器eventListener(继承于AnalysisEventListener) 存入ExcelAnalyserImpl类的AnalysisContext属性

packagecom.alibaba.excel.analysis;importcom.alibaba.excel.analysis.v03.XlsSaxAnalyser;importcom.alibaba.excel.analysis.v07.XlsxSaxAnalyser;importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.context.AnalysisContextImpl;importcom.alibaba.excel.event.AnalysisEventListener;importcom.alibaba.excel.exception.ExcelAnalysisException;importcom.alibaba.excel.metadata.Sheet;importcom.alibaba.excel.modelbuild.ModelBuildEventListener;importcom.alibaba.excel.support.ExcelTypeEnum;importjava.io.InputStream;importjava.util.List;/***@authorjipengfei*/

public class ExcelAnalyserImpl implementsExcelAnalyser {privateAnalysisContext analysisContext;privateBaseSaxAnalyser saxAnalyser;publicExcelAnalyserImpl(InputStream inputStream, ExcelTypeEnum excelTypeEnum, Object custom,

AnalysisEventListener eventListener,booleantrim) {

analysisContext= newAnalysisContextImpl(inputStream, excelTypeEnum, custom,

eventListener, trim);

}privateBaseSaxAnalyser getSaxAnalyser() {if (saxAnalyser != null) {return this.saxAnalyser;

}try{if (analysisContext.getExcelType() != null) {switch(analysisContext.getExcelType()) {caseXLS:this.saxAnalyser = newXlsSaxAnalyser(analysisContext);break;caseXLSX:this.saxAnalyser = newXlsxSaxAnalyser(analysisContext);break;

}

}else{try{this.saxAnalyser = newXlsxSaxAnalyser(analysisContext);

}catch(Exception e) {if (!analysisContext.getInputStream().markSupported()) {throw newExcelAnalysisException("Xls must be available markSupported,you can do like this new "

+ "BufferedInputStream(new FileInputStream(\"/xxxx\")) ");

}this.saxAnalyser = newXlsSaxAnalyser(analysisContext);

}

}

}catch(Exception e) {throw new ExcelAnalysisException("File type error,io must be available markSupported,you can do like "

+ "this new BufferedInputStream(new FileInputStream(\\\"/xxxx\\\")) \"", e);

}return this.saxAnalyser;

}

@Overridepublic voidanalysis(Sheet sheetParam) {

analysisContext.setCurrentSheet(sheetParam);

analysis();

}

@Overridepublic voidanalysis() {

BaseSaxAnalyser saxAnalyser=getSaxAnalyser();

appendListeners(saxAnalyser);

saxAnalyser.execute();

analysisContext.getEventListener().doAfterAllAnalysed(analysisContext);

}

@Overridepublic ListgetSheets() {

BaseSaxAnalyser saxAnalyser=getSaxAnalyser();

saxAnalyser.cleanAllListeners();returnsaxAnalyser.getSheets();

}private voidappendListeners(BaseSaxAnalyser saxAnalyser) {

saxAnalyser.cleanAllListeners();if (analysisContext.getCurrentSheet() != null && analysisContext.getCurrentSheet().getClazz() != null) {

saxAnalyser.appendLister("model_build_listener", newModelBuildEventListener());

}if (analysisContext.getEventListener() != null) {

saxAnalyser.appendLister("user_define_listener", analysisContext.getEventListener());

}

}

}

View Code

packagecom.alibaba.excel.analysis;importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.event.AnalysisEventListener;importcom.alibaba.excel.event.AnalysisEventRegisterCenter;importcom.alibaba.excel.event.OneRowAnalysisFinishEvent;importcom.alibaba.excel.metadata.Sheet;importcom.alibaba.excel.util.TypeUtil;importjava.util.ArrayList;importjava.util.LinkedHashMap;importjava.util.List;importjava.util.Map;/***@authorjipengfei*/

public abstract class BaseSaxAnalyser implementsAnalysisEventRegisterCenter, ExcelAnalyser {protectedAnalysisContext analysisContext;private LinkedHashMap listeners = new LinkedHashMap();/*** execute method*/

protected abstract voidexecute();

@Overridepublic voidappendLister(String name, AnalysisEventListener listener) {if (!listeners.containsKey(name)) {

listeners.put(name, listener);

}

}

@Overridepublic voidanalysis(Sheet sheetParam) {

execute();

}

@Overridepublic voidanalysis() {

execute();

}/**

*/@Overridepublic voidcleanAllListeners() {

listeners= new LinkedHashMap();

}

@Overridepublic voidnotifyListeners(OneRowAnalysisFinishEvent event) {

analysisContext.setCurrentRowAnalysisResult(event.getData());/**Parsing header content **/

if (analysisContext.getCurrentRowNum()

analysisContext.buildExcelHeadProperty(null,

(List)analysisContext.getCurrentRowAnalysisResult());

}

}else{

List content = converter((List)event.getData());/**Parsing Analyze the body content **/analysisContext.setCurrentRowAnalysisResult(content);if (listeners.size() == 1) {

analysisContext.setCurrentRowAnalysisResult(content);

}/**notify all event listeners **/

for (Map.Entryentry : listeners.entrySet()) {

entry.getValue().invoke(analysisContext.getCurrentRowAnalysisResult(), analysisContext);

}

}

}private List converter(Listdata) {

List list = new ArrayList();if (data != null) {for(String str : data) {

list.add(TypeUtil.formatFloat(str));

}

}returnlist;

}

}

View Code

通过ExcelAnalyserImpl类的getSaxAnalyser()方法获取Excel是.xls还是.xlsx类型后,将ExcelAnalyserImpl的analysisContext属性赋给BaseSaxAnalyser类的analysisContext属性,再用appendListeners()方法封装默认的监听器和自定义的监听器到BaseSaxAnalyser类 的listeners属性中,到此为止,BaseSaxAnalyser的两个属性:analysisContext、listeners已经分别存有file文件、excel枚举类型和监听器等值

接下来讲一个DefaultHandler类(核心类),该类是SAX事件解析器,底层在磁盘中解析文件并一行一行的读取,并且会依次执行startDocument()、startElement()、characters()、endElement()、endDocument()方法

packageorg.xml.sax.helpers;importjava.io.IOException;importorg.xml.sax.InputSource;importorg.xml.sax.Locator;importorg.xml.sax.Attributes;importorg.xml.sax.EntityResolver;importorg.xml.sax.DTDHandler;importorg.xml.sax.ContentHandler;importorg.xml.sax.ErrorHandler;importorg.xml.sax.SAXException;importorg.xml.sax.SAXParseException;public classDefaultHandlerimplementsEntityResolver, DTDHandler, ContentHandler, ErrorHandler

{publicInputSource resolveEntity (String publicId, String systemId)throwsIOException, SAXException

{return null;

}public voidnotationDecl (String name, String publicId, String systemId)throwsSAXException

{//no op

}public voidunparsedEntityDecl (String name, String publicId,

String systemId, String notationName)throwsSAXException

{//no op

}public voidsetDocumentLocator (Locator locator)

{//no op

}public voidstartDocument ()throwsSAXException

{//no op

}public voidendDocument ()throwsSAXException

{//no op

}public voidstartPrefixMapping (String prefix, String uri)throwsSAXException

{//no op

}public voidendPrefixMapping (String prefix)throwsSAXException

{//no op

}public voidstartElement (String uri, String localName,

String qName, Attributes attributes)throwsSAXException

{//no op

}public voidendElement (String uri, String localName, String qName)throwsSAXException

{//no op

}public void characters (char ch[], int start, intlength)throwsSAXException

{//no op

}public void ignorableWhitespace (char ch[], int start, intlength)throwsSAXException

{//no op

}public voidprocessingInstruction (String target, String data)throwsSAXException

{//no op

}public voidskippedEntity (String name)throwsSAXException

{//no op

}public voidwarning (SAXParseException e)throwsSAXException

{//no op

}public voiderror (SAXParseException e)throwsSAXException

{//no op

}public voidfatalError (SAXParseException e)throwsSAXException

{throwe;

}

}//end of DefaultHandler.java

View Code

通过XlsxRowHandler类继承DefaultHandler类,将每一行读取的行数和数据分别存入curCol和curRowContent属性中

读取完之后,通过后置方法endElement()方法,唤醒最初自己定义的监听器ExcelListener,并将读取到的行数、数据等赋给该监听器

packagecom.alibaba.excel.analysis.v07;importcom.alibaba.excel.annotation.FieldType;importcom.alibaba.excel.constant.ExcelXmlConstants;importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.event.AnalysisEventRegisterCenter;importcom.alibaba.excel.event.OneRowAnalysisFinishEvent;importcom.alibaba.excel.util.PositionUtils;importorg.apache.poi.xssf.model.SharedStringsTable;importorg.apache.poi.xssf.usermodel.XSSFRichTextString;importorg.xml.sax.Attributes;importorg.xml.sax.SAXException;importorg.xml.sax.helpers.DefaultHandler;importjava.util.Arrays;import static com.alibaba.excel.constant.ExcelXmlConstants.*;/***

*@authorjipengfei*/

public class XlsxRowHandler extendsDefaultHandler {privateString currentCellIndex;privateFieldType currentCellType;private intcurRow;private intcurCol;private String[] curRowContent = new String[20];privateString currentCellValue;privateSharedStringsTable sst;privateAnalysisContext analysisContext;privateAnalysisEventRegisterCenter registerCenter;publicXlsxRowHandler(AnalysisEventRegisterCenter registerCenter, SharedStringsTable sst,

AnalysisContext analysisContext) {this.registerCenter =registerCenter;this.analysisContext =analysisContext;this.sst =sst;

}

@Overridepublic void startElement(String uri, String localName, String name, Attributes attributes) throwsSAXException {

setTotalRowCount(name, attributes);

startCell(name, attributes);

startCellValue(name);

}private voidstartCellValue(String name) {if (name.equals(CELL_VALUE_TAG) ||name.equals(CELL_VALUE_TAG_1)) {//initialize current cell value

currentCellValue = "";

}

}private voidstartCell(String name, Attributes attributes) {if(ExcelXmlConstants.CELL_TAG.equals(name)) {

currentCellIndex=attributes.getValue(ExcelXmlConstants.POSITION);int nextRow =PositionUtils.getRow(currentCellIndex);if (nextRow >curRow) {

curRow=nextRow;//endRow(ROW_TAG);

}

analysisContext.setCurrentRowNum(curRow);

curCol=PositionUtils.getCol(currentCellIndex);

String cellType= attributes.getValue("t");

currentCellType=FieldType.EMPTY;if (cellType != null && cellType.equals("s")) {

currentCellType=FieldType.STRING;

}

}

}private void endCellValue(String name) throwsSAXException {//ensure size

if (curCol >=curRowContent.length) {

curRowContent= Arrays.copyOf(curRowContent, (int)(curCol * 1.5));

}if(CELL_VALUE_TAG.equals(name)) {switch(currentCellType) {caseSTRING:int idx =Integer.parseInt(currentCellValue);

currentCellValue= newXSSFRichTextString(sst.getEntryAt(idx)).toString();

currentCellType=FieldType.EMPTY;break;

}

curRowContent[curCol]=currentCellValue;

}else if(CELL_VALUE_TAG_1.equals(name)) {

curRowContent[curCol]=currentCellValue;

}

}

@Overridepublic void endElement(String uri, String localName, String name) throwsSAXException {

endRow(name);

endCellValue(name);

}

@Overridepublic void characters(char[] ch, int start, int length) throwsSAXException {

currentCellValue+= newString(ch, start, length);

}private voidsetTotalRowCount(String name, Attributes attributes) {if(DIMENSION.equals(name)) {

String d=attributes.getValue(DIMENSION_REF);

String totalStr= d.substring(d.indexOf(":") + 1, d.length());

String c= totalStr.toUpperCase().replaceAll("[A-Z]", "");

analysisContext.setTotalCount(Integer.parseInt(c));

}

}private voidendRow(String name) {if(name.equals(ROW_TAG)) {

registerCenter.notifyListeners(newOneRowAnalysisFinishEvent(curRowContent,curCol));

curRowContent= new String[20];

}

}

}

View Code

此时回到ExcelListener监听器,便可以去取到每一行的数据存入datas属性中,并且对比导入的表头和模板表头是否一致等

到此,可以读到excel的每一行数据,用于业务处理。

easypoi必填项_EasyExcel对Excel文件的解析过程相关推荐

  1. easypoi必填项_GitHub - wangjingting/poi-utils: help you more easy read or write excel file

    author:邹凯明 poi-utils help you more easy reading or writing[TODO] excel file 本工具包入口类是ExcelFileUtil,对于 ...

  2. EasyExcel对Excel文件的解析过程

    POI与easyExcel的区别: POI是通过WorkBook来解析表格文件的,虽然内部有使用到Sax模式,能后大大的提高效率,但是要解析大文件(10w,100w行)的话很容易出现OOM(内存溢出) ...

  3. input 必填项(文本框text,文件file)

    批量校验input 必填项不为空,代码如下: 一.前端页面js: //批量校验input必填项 function check(){var item = $("input[mustwrite= ...

  4. 如何设置dedecms自定义表单必填项?

    用dedecms自定义表单可以制作一个简单的预约系统,有些相关信息需要设置为必填项,比如联系方式,没有留下真实的电话或其他信息,以后要怎么联系到你的客户.那我们要如何设置织梦cms自定义表单必填项呢? ...

  5. eas 税率修改_如何修改金蝶EAS业务单据中的字段为必填项

    关于金蝶EAS的使用所需要注重到的方面有很多,当然在具体使用的时候还是要看不同的功能,因为功能不同在后续的效果上也不同,尤其是在金蝶EAS的管理功能中所涉及到的层面也更多,因此在相关的因素上也一定要注 ...

  6. div.php织梦自定义表判断不能为空,织梦自定义表单字段为必填项的教程

    织梦自定义表单用的最多的就是制作留言板,报名等功能,但是添加的字段不填写就能提交,容易被恶意提交,为了防止这些,我们可以把这些字段选项设定为必填项. 方法如下: 1. 用php验证 在plus/diy ...

  7. ExtJS4为form表单必填项添加红色*标识

    通常情况下,ExtJS4的form表单必填项在输入状态下会有特殊提示,非输入状态下,显示却和其他项没有任何区别.为使必填项更加容易区分,我们需要根据allowBlank的属性值,为form表单中的必填 ...

  8. 【CSS系列】CSS 实现必填项前/后添加红色星号

    在一些必填项的标签加星,来提示用户,怎么实现呢?请看本文介绍的两种方法. 1 . 常规写法 <label><span style="color:red;"> ...

  9. layui自带验证体系:手机号验证、邮箱验证、必填项非空验证、数字验证(含代码、案例)

    layui自带验证体系:手机号验证.邮箱验证.必填项非空验证.数字验证(含代码.案例) 案例 · 截图: 实例代码: <!DOCTYPE html> <html> <he ...

最新文章

  1. [转]后期-快速消除痘痘,完美修复MM肌肤
  2. 在Asp.Net中从sqlserver检索(retrieve)图片
  3. bat java cp_批处理中Copy与Xcopy命令的区别小结
  4. 如何先执行input (checkbox,radio)再执行函数
  5. 控制台双缓冲防闪烁--功能封装
  6. 计算机网络把许多什么连接在一起,计算机网络
  7. redhat7 上安装dummynet
  8. Java CookBook(一)
  9. 九款Web服务器性能压力测试工具
  10. 用标准普尔家庭资产象限图规划收入分配
  11. 欧洲航天局遭匿名者(Anonymous)攻击泄露大量数据
  12. 2-10配置Linux网络
  13. 结构化数据与非结构化数据的区别
  14. 用python计算工资工资_python税后工资计算器
  15. 三维重建入门学习————建模软件Blender入门篇
  16. 【会声会影】导入的srt字幕文件,如何快速批量调整字体及大小
  17. linux系统的系统性学习 (持续更新)
  18. 投资型人寿保险的利与弊
  19. 基于jsp+servler+css的登录界面
  20. unity围绕物体旋转加角度限制

热门文章

  1. 多apk发布的Android程序的adb安装——adb install-multiple
  2. FPGA CY7C68013A调试经验总结
  3. Spring Webflux - 03 Webflux编程模型
  4. 重要通知:2020年12月CFA最后一次笔考报名截止8月19日!
  5. vite+vue3中使用mock模拟数据
  6. 上市公司管理层短视指标(2007-2020)
  7. php模拟腾讯微校第三方认证,利用腾讯云服务器进行微校开放平台开发
  8. 怎么解决sockjs.js?9be2:1609 GET http://192.168.0.133:8080/sockjs-node/info?t=1630545142551 net::ERR_NETW
  9. c语言程序设计用指纹解锁门,基于指纹识别技术的校园门禁应用
  10. 不用GPU搭建一个漫画自动翻译网站