Bladeren bron

增加文件导入表结构,支持多表同时导入。未做表名校验

baolei 1 jaar geleden
bovenliggende
commit
5a93e3a44b

+ 30 - 0
src/main/java/com/goer/common/exception/BusinessException.java

@@ -0,0 +1,30 @@
+package com.goer.common.exception;
+
+/**
+ * 业务异常
+ * 
+ * @author hocy
+ */
+public class BusinessException extends RuntimeException
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final String message;
+
+    public BusinessException(String message)
+    {
+        this.message = message;
+    }
+
+    public BusinessException(String message, Throwable e)
+    {
+        super(message, e);
+        this.message = message;
+    }
+
+    @Override
+    public String getMessage()
+    {
+        return message;
+    }
+}

+ 238 - 5
src/main/java/com/goer/common/utils/poi/ExcelUtil.java

@@ -1,10 +1,6 @@
 package com.goer.common.utils.poi;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
@@ -20,6 +16,8 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
+
+import com.goer.common.exception.BusinessException;
 import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
 import org.apache.poi.hssf.usermodel.HSSFPicture;
 import org.apache.poi.hssf.usermodel.HSSFPictureData;
@@ -102,6 +100,16 @@ public class ExcelUtil<T>
     private Type type;
 
     /**
+     * 模板文件
+     */
+    private String templateFile;
+
+    /**
+     * 模板文件
+     */
+    private int writeStartRow;
+
+    /**
      * 工作薄对象
      */
     private Workbook wb;
@@ -1386,4 +1394,229 @@ public class ExcelUtil<T>
         }
         return sheetIndexPicMap;
     }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel模板文件
+     *
+     * @param list 导出数据集合
+     * @param templateFileName 模板文件名
+     * @return 结果
+     */
+    public AjaxResult exportExcelWithTemplate(List<T> list, String templateFileName) throws Exception
+    {
+        this.initWithTemplate(list, templateFileName, Type.EXPORT);
+        return exportExcelWithTemplate();
+    }
+
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *   模板文件第一行为标题行,第二行为格式行,其他行会复制第二行的格式
+     *
+     * @return 结果
+     */
+    public AjaxResult exportExcelWithTemplate()
+    {
+        OutputStream out = null;
+        try
+        {
+            // 取出一共有多少个sheet.
+            double sheetNo = Math.ceil(list.size() / sheetSize);
+            for (int index = 1; index <= sheetNo; index++) {
+                wb.cloneSheet(0);
+                wb.setSheetName(index, sheetName + index);
+            }
+
+            for (int index = 0; index <= sheetNo; index++)
+            {
+                sheet = wb.getSheetAt(index);
+
+                if (Type.EXPORT.equals(type))
+                {
+                    fillExcelDataWithTemple(index);
+                }
+            }
+            String filename = encodingFilename(sheetName);
+            out = new FileOutputStream(getAbsoluteFile(filename));
+            wb.write(out);
+            return AjaxResult.success(filename);
+        }
+        catch (Exception e)
+        {
+            log.error("导出Excel异常{}", e.getMessage());
+            throw new BusinessException("导出Excel失败,请联系网站管理员!");
+        }
+        finally
+        {
+            if (wb != null)
+            {
+                try
+                {
+                    wb.close();
+                }
+                catch (IOException e1)
+                {
+                    e1.printStackTrace();
+                }
+            }
+            if (out != null)
+            {
+                try
+                {
+                    out.close();
+                }
+                catch (IOException e1)
+                {
+                    e1.printStackTrace();
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 填充数据到模板文件
+     *
+     * @param index 序号
+     */
+    public void fillExcelDataWithTemple(int index)
+    {
+        int startNo = index * sheetSize;
+        int endNo = Math.min(startNo + sheetSize, list.size());
+        // 写入各条记录,每条记录对应excel表中的一行
+        CellStyle cs = wb.createCellStyle();
+        cs.setAlignment(HorizontalAlignment.CENTER);
+        cs.setVerticalAlignment(VerticalAlignment.CENTER);
+        int rowIndex = writeStartRow;
+        for (int i = startNo; i < endNo; i++)
+        {
+            Row row;
+            if (rowIndex == writeStartRow) {
+                row = sheet.getRow(rowIndex);
+            } else {
+                row = sheet.createRow(rowIndex);
+            }
+            // 得到导出对象.
+            T vo = (T) list.get(i);
+            int column = 0;
+            for (Object[] os : fields)
+            {
+                Field field = (Field) os[0];
+                Excel excel = (Excel) os[1];
+                // 设置实体类私有属性可访问
+                field.setAccessible(true);
+                this.fillCell(excel, row, vo, field, rowIndex, column++);
+            }
+
+            rowIndex = rowIndex + 1;
+        }
+    }
+
+
+    /**
+     * 填充单元格
+     */
+    public Cell fillCell(Excel attr, Row row, T vo, Field field, int rowIndex, int column)
+    {
+        Cell cell = null;
+        try
+        {
+            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
+            if (attr.isExport())
+            {
+                // 获取cell
+                if (rowIndex == writeStartRow) {
+                    cell = row.getCell(column);
+                } else {
+                    CellStyle cellStyle = sheet.getRow(writeStartRow).getCell(column).getCellStyle();
+                    cell = row.createCell(column);
+                    cell.setCellStyle(cellStyle);
+                }
+                // 用于读取对象中的属性
+                Object value = getTargetValue(vo, field, attr);
+                String dateFormat = attr.dateFormat();
+                String readConverterExp = attr.readConverterExp();
+                // 取得类型,并根据对象类型设置值.
+                Class<?> fieldType = field.getType();
+                if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
+                {
+                    cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
+                }
+                else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
+                {
+                    cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp));
+                }
+                else
+                {
+                    if ((Integer.TYPE == fieldType) || (Integer.class == fieldType))
+                    {
+                        cell.setCellType(CellType.NUMERIC);
+                    } else {
+                        cell.setCellType(CellType.STRING);
+                    }
+                    // 如果数据存在就填入,不存在填入空格.
+                    cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            log.error("导出Excel失败{}", e);
+        }
+        return cell;
+    }
+
+    /**
+     * 解析导出值 0=男,1=女,2=未知
+     *
+     * @param propertyValue 参数值
+     * @param converterExp 翻译注解
+     * @return 解析后值
+     * @throws Exception
+     */
+    public static String convertByExp(String propertyValue, String converterExp) throws Exception
+    {
+        try
+        {
+            String[] convertSource = converterExp.split(",");
+            for (String item : convertSource)
+            {
+                String[] itemArray = item.split("=");
+                if (itemArray[0].equals(propertyValue))
+                {
+                    return itemArray[1];
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        return propertyValue;
+    }
+
+    public void initWithTemplate(List<T> list, String templateFile, Type type) throws Exception
+    {
+        if (list == null)
+        {
+            list = new ArrayList<T>();
+        }
+        this.list = list;
+        this.templateFile = templateFile;
+        this.type = type;
+        createExcelField();
+        readTemplateFile();
+    }
+
+    /**
+     * 读取模板文件
+     */
+    public void readTemplateFile() throws Exception
+    {
+        File fi = new File(this.templateFile);
+        FileInputStream is = new FileInputStream(fi);
+        this.wb = WorkbookFactory.create(is);
+        sheetName = this.wb.getSheetAt(0).getSheetName();
+    }
+
 }

+ 36 - 0
src/main/java/com/goer/project/model/modelEntity/controller/ModelEntityController.java

@@ -8,6 +8,7 @@ import com.goer.common.command.AbstractDBCommand;
 import com.goer.common.command.ExecResult;
 import com.goer.common.command.impl.DBReverseGetAllTablesListImpl;
 import com.goer.common.command.impl.PingLoadDriverClassImpl;
+import com.goer.common.utils.StringUtils;
 import com.goer.project.model.modelDbs.domain.ModelDbs;
 import com.goer.project.model.modelDbs.service.IModelDbsService;
 import com.goer.project.model.modelField.domain.ModelField;
@@ -29,6 +30,8 @@ import com.goer.framework.web.controller.BaseController;
 import com.goer.framework.web.domain.AjaxResult;
 import com.goer.common.utils.poi.ExcelUtil;
 import com.goer.framework.web.page.TableDataInfo;
+import org.springframework.web.multipart.MultipartFile;
+import com.goer.framework.config.GoerConfig;
 
 /**
  * 数据Controller
@@ -355,4 +358,37 @@ public class ModelEntityController extends BaseController
     {
         return modelEntityService.checkEntityCodeUnique(modelEntity);
     }
+
+
+    /**
+     * 下载模板
+     */
+    @RequiresPermissions("vendor:order:importExpress")
+    @GetMapping("/importTemplate")
+    @ResponseBody
+    public AjaxResult importTemplate() throws Exception {
+        ExcelUtil<ModelEntity> util = new ExcelUtil<ModelEntity>(ModelEntity.class);
+        String templateFile = GoerConfig.getProfile() + File.separator + "importTables.xlsx";
+        return util.exportExcelWithTemplate(null, templateFile);
+    }
+
+    /**
+     * 导入快递编号文件
+     */
+    @RequiresPermissions("vendor:order:importExpress")
+    @PostMapping("/importExpress")
+    @ResponseBody
+    @Log(title = "导入表结构文件 ", businessType = BusinessType.INSERT)
+    public AjaxResult importExpress(MultipartFile file, Long prjId) {
+
+        try {
+
+            modelEntityService.importTables(file.getInputStream(), prjId);
+
+            return AjaxResult.success();
+        } catch (Exception e) {
+            return AjaxResult.error(StringUtils.isEmpty(e.getMessage()) ? e.getCause().getMessage() : e.getMessage());
+        }
+    }
+
 }

+ 4 - 0
src/main/java/com/goer/project/model/modelEntity/service/IModelEntityService.java

@@ -1,9 +1,11 @@
 package com.goer.project.model.modelEntity.service;
 
+import java.io.InputStream;
 import java.util.List;
 
 import com.goer.common.chiner.model.TableEntity;
 import com.goer.project.model.modelEntity.domain.ModelEntity;
+import org.apache.poi.ss.formula.functions.T;
 
 /**
  * 数据Service接口
@@ -74,4 +76,6 @@ public interface IModelEntityService
 
     public String checkEntityCodeUnique(ModelEntity modelEntity);
 
+    public int importTables(InputStream is, Long projectId) throws Exception;
+
 }

+ 256 - 4
src/main/java/com/goer/project/model/modelEntity/service/impl/ModelEntityServiceImpl.java

@@ -1,9 +1,11 @@
 package com.goer.project.model.modelEntity.service.impl;
 
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.util.*;
 
 import com.alibaba.fastjson.JSON;
 import com.goer.common.chiner.model.ColumnField;
@@ -13,7 +15,13 @@ import com.goer.common.command.ExecResult;
 import com.goer.common.command.impl.DBReverseGetTableDDLImpl;
 import com.goer.common.utils.DateUtils;
 import com.goer.common.utils.StringUtils;
+import com.goer.common.utils.file.FileUtils;
 import com.goer.common.utils.fisok.raw.kit.JSONKit;
+import com.goer.common.utils.poi.ExcelHandlerAdapter;
+import com.goer.common.utils.poi.ExcelUtil;
+import com.goer.common.utils.reflect.ReflectUtils;
+import com.goer.common.utils.security.ShiroUtils;
+import com.goer.framework.aspectj.lang.annotation.Excel;
 import com.goer.project.model.dataType.domain.DataType;
 import com.goer.project.model.dataType.mapper.DataTypeMapper;
 import com.goer.project.model.modelDbs.domain.ModelDbs;
@@ -21,6 +29,12 @@ import com.goer.project.model.modelDbs.mapper.ModelDbsMapper;
 import com.goer.project.model.modelField.domain.ModelField;
 import com.goer.project.model.modelField.mapper.ModelFieldMapper;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.formula.functions.T;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.goer.project.model.modelEntity.mapper.ModelEntityMapper;
@@ -28,6 +42,9 @@ import com.goer.project.model.modelEntity.domain.ModelEntity;
 import com.goer.project.model.modelEntity.service.IModelEntityService;
 import com.goer.common.utils.text.Convert;
 
+import static com.goer.common.utils.poi.ExcelUtil.getSheetPictures03;
+import static com.goer.common.utils.poi.ExcelUtil.getSheetPictures07;
+
 /**
  * 数据Service业务层处理
  * 
@@ -308,4 +325,239 @@ public class ModelEntityServiceImpl implements IModelEntityService
         }
         return "0";
     }
+
+    /**
+     * 导入文件表结构,转换成list
+     *
+     * @param is 输入流
+     * @return 转换后集合
+     */
+    public int importTables(InputStream is, Long projectId) throws Exception
+    {
+
+        int rtn = 0;
+
+        List<ModelEntity> mes = new ArrayList<>();
+
+        Date nowDate = DateUtils.getNowDate();
+
+        Workbook wb = WorkbookFactory.create(is);
+        // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
+        int sheetCnt = wb.getNumberOfSheets();
+
+        if (sheetCnt == 0) {
+            throw new IOException("文件无sheet");
+        }
+
+        Sheet sheet = null;
+        ModelEntity cond = new ModelEntity();
+        cond.setProjectId(projectId);
+        cond.setVersionId(0L);
+        int intEntitySort = modelEntityMapper.selectMaxEntitySort(cond) + 1;
+
+        for (int i=0;i<sheetCnt;i++) {
+
+            sheet = wb.getSheetAt(i);
+            // 获取表名称(中文)
+            Row row1 = sheet.getRow(1);
+            String tableName = null;
+            if (row1.getCell(2) != null) {
+                tableName = getCellValue(row1,2).toString();
+            }
+            // 获取表逻辑名(英文)
+            Row row2 = sheet.getRow(2);
+            String tableLogicName = null;
+            if (row2.getCell(2)!= null) {
+                tableLogicName = getCellValue(row2,2).toString();
+            }
+
+            // 获取描述(中文)
+            Row row3 = sheet.getRow(3);
+            String tableDesc = null;
+            if (row3.getCell(2)!= null) {
+                tableDesc = getCellValue(row3,2).toString();
+            }
+
+            if (StringUtils.isEmpty(tableName) || StringUtils.isEmpty(tableLogicName)) {
+                throw new IOException("[" + sheet.getSheetName() + "]sheet无表名。");
+            }
+
+            ModelEntity me = new ModelEntity();
+            me.setProjectId(projectId);
+            me.setVersionId(0L);
+            me.setEntityName(tableName);
+            me.setEntityCode(tableLogicName);
+            me.setRemark(tableDesc);
+            me.setEntitySort(intEntitySort);
+            me.setParentVersionId(0L);
+            me.setCreateBy(ShiroUtils.getLoginName());
+            me.setCreateTime(nowDate);
+            me.setUpdateBy(ShiroUtils.getLoginName());
+            me.setUpdateTime(nowDate);
+
+            List<ModelField> mfs = new ArrayList<>();
+            int rowIdx = 6;
+            int sort = 1;
+            while (StringUtils.isNotEmpty(getCellValue(sheet.getRow(rowIdx),1).toString()))
+            {
+                ModelField mf = new ModelField();
+
+                // 字段名(中文)
+                String fieldName = null;
+                if (sheet.getRow(rowIdx).getCell(2) != null) {
+                    fieldName = getCellValue(sheet.getRow(rowIdx),2).toString();
+                };
+                if (StringUtils.isEmpty(fieldName)) {
+                    throw new IOException("[" + sheet.getSheetName() + "]sheet,第"+ (rowIdx + 1) + "行,字段名为空。");
+                }
+
+                // 逻辑名(英文)
+                String fieldLogicName = null;
+                if (sheet.getRow(rowIdx).getCell(3) != null) {
+                    fieldLogicName = getCellValue(sheet.getRow(rowIdx),3).toString();
+                };
+                if (StringUtils.isEmpty(fieldLogicName)) {
+                    throw new IOException("[" + sheet.getSheetName() + "]sheet,第"+ (rowIdx+1) + "行,逻辑名为空。");
+                }
+                // 类型(请选择)
+                String fieldType = null;
+                if (sheet.getRow(rowIdx).getCell(4) != null) {
+                    fieldType = getCellValue(sheet.getRow(rowIdx),4).toString();
+                };
+                if (StringUtils.isEmpty(fieldType)) {
+                    throw new IOException("[" + sheet.getSheetName() + "]sheet,第"+ (rowIdx+1) + "行,类型为空。");
+                }
+                // 主键(请选择)
+                String fieldIsKey = null;
+                if (sheet.getRow(rowIdx).getCell(5) != null) {
+                    fieldIsKey = getCellValue(sheet.getRow(rowIdx), 5).toString();
+                }
+                if (StringUtils.isEmpty(fieldIsKey)) {
+                    fieldIsKey = "N";
+                }
+                // 非空(请选择默认为N)
+                String fieldNotNull = null;
+                if (sheet.getRow(rowIdx).getCell(6) != null) {
+                    fieldNotNull = getCellValue(sheet.getRow(rowIdx), 6).toString();
+                }
+                if (StringUtils.isEmpty(fieldNotNull)) {
+                    fieldNotNull = "N";
+                }
+                // 自增(请选择默认为N)
+                String fieldAuto = null;
+                if (sheet.getRow(rowIdx).getCell(7) != null) {
+                    fieldAuto = getCellValue(sheet.getRow(rowIdx), 7).toString();
+                }
+                if (StringUtils.isEmpty(fieldAuto)) {
+                    fieldAuto = "N";
+                }
+                // 默认值
+                String fieldDefaultValue = null;
+                if (sheet.getRow(rowIdx).getCell(8) != null) {
+                    fieldDefaultValue = getCellValue(sheet.getRow(rowIdx), 8).toString();
+                }
+                // 备注
+                String fieldRemark = null;
+                if (sheet.getRow(rowIdx).getCell(9) != null) {
+                    fieldRemark = getCellValue(sheet.getRow(rowIdx), 9).toString();
+                }
+
+                mf.setProjectId(projectId);
+                mf.setVersionId(0L);
+                mf.setFieldName(fieldName);
+                mf.setFieldCode(fieldLogicName);
+                mf.setFieldType(fieldType);
+                mf.setFieldPk(fieldIsKey);
+                mf.setNotNull(fieldNotNull);
+                mf.setAutoIncrement(fieldAuto);
+                mf.setDefaultValue(fieldDefaultValue);
+                mf.setFieldSort(sort);
+                mf.setRemark(fieldRemark);
+                mf.setParentVersionId(0L);
+                mf.setCreateBy(ShiroUtils.getLoginName());
+                mf.setCreateTime(nowDate);
+                mf.setUpdateBy(ShiroUtils.getLoginName());
+                mf.setUpdateTime(nowDate);
+
+                mfs.add(mf);
+                rowIdx = rowIdx +1;
+                sort = sort + 1;
+            }
+
+            me.setFields(mfs);
+            intEntitySort = intEntitySort + 1;
+            mes.add(me);
+        }
+
+        for (ModelEntity modelEntity : mes) {
+            rtn = rtn + modelEntityMapper.insertModelEntity(modelEntity);
+
+            for (ModelField modelField : modelEntity.getFields()) {
+                modelField.setEntityId(modelEntity.getId());
+                rtn = rtn + modelFieldMapper.insertModelField(modelField);
+            }
+        }
+
+        return rtn;
+    }
+
+    /**
+     * 获取单元格值
+     *
+     * @param row 获取的行
+     * @param column 获取单元格列号
+     * @return 单元格值
+     */
+    public Object getCellValue(Row row, int column)
+    {
+        if (row == null)
+        {
+            return "";
+        }
+        Object val = "";
+        try
+        {
+            Cell cell = row.getCell(column);
+            if (StringUtils.isNotNull(cell))
+            {
+                if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA)
+                {
+                    val = cell.getNumericCellValue();
+                    if (DateUtil.isCellDateFormatted(cell))
+                    {
+                        val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
+                    }
+                    else
+                    {
+                        if ((Double) val % 1 != 0)
+                        {
+                            val = new BigDecimal(val.toString());
+                        }
+                        else
+                        {
+                            val = new DecimalFormat("0").format(val);
+                        }
+                    }
+                }
+                else if (cell.getCellType() == CellType.STRING)
+                {
+                    val = cell.getStringCellValue();
+                }
+                else if (cell.getCellType() == CellType.BOOLEAN)
+                {
+                    val = cell.getBooleanCellValue();
+                }
+                else if (cell.getCellType() == CellType.ERROR)
+                {
+                    val = cell.getErrorCellValue();
+                }
+
+            }
+        }
+        catch (Exception e)
+        {
+            return val;
+        }
+        return val;
+    }
 }

+ 2 - 2
src/main/resources/application.yml

@@ -8,8 +8,8 @@ goer:
   copyrightYear: 2022
   # 实例演示开关
   demoEnabled: true
-  # 文件路径 示例( Windows配置D:/goer/uploadPath,Linux配置 /home/goer/uploadPath
-  profile: D:/goer/uploadPath
+  # 文件路径 示例( Windows配置D:/goer/uploadPath,Linux配置 /home/app/goods2c/public
+  profile: D:/goods2c/public
   # 获取ip地址开关
   addressEnabled: false
 

BIN
src/main/resources/static/public/importTables.xlsx


+ 3 - 3
src/main/resources/templates/model/modelEntity/copy.html

@@ -12,19 +12,19 @@
         <div class="form-group">
             <label class="col-sm-3 control-label is-required">表名称:</label>
             <div class="col-sm-8">
-                <input id="entityName" name="entityName" placeholder="表名称" class="form-control" type="text" required>
+                <input id="entityName" name="entityName" th:field="*{entityName}" placeholder="表名称" class="form-control" type="text" required>
             </div>
         </div>
         <div class="form-group">
             <label class="col-sm-3 control-label is-required">表逻辑名:</label>
             <div class="col-sm-8">
-                <input id="entityCode" name="entityCode" placeholder="m_tablename" class="form-control" type="text" required>
+                <input id="entityCode" name="entityCode" th:field="*{entityCode}"  placeholder="m_tablename" class="form-control" type="text" required>
             </div>
         </div>
         <div class="form-group">
             <label class="col-sm-3 control-label">描述:</label>
             <div class="col-sm-8">
-                <textarea id="remark" name="remark" class="form-control"></textarea>
+                <textarea name="remark" class="form-control">[[*{remark}]]</textarea>
             </div>
         </div>
     </form>

+ 21 - 1
src/main/resources/templates/model/modelEntity/modelEntity.html

@@ -58,6 +58,9 @@
                 <a class="btn btn-warning" onclick="importTable()" shiro:hasPermission="model:modelEntity:import">
                     <i class="fa fa-upload"></i> 从数据库导入
                 </a>
+                <a class="btn btn-primary" onclick="$.table.importExcel()" shiro:hasPermission="model:modelEntity:import">
+                    <i class="fa fa-edit"></i> 从文件导入
+                </a>
                 <a class="btn btn-danger" onclick="closeItem()">
                     <i class="fa fa-reply-all"></i> 关闭
                 </a>
@@ -70,7 +73,22 @@
         </div>
     </div>
     <th:block th:include="include :: footer" />
-     <th:block th:include="include :: bootstrap-table-reorder-rows-js" />
+    <th:block th:include="include :: bootstrap-table-reorder-rows-js" />
+     <!-- 导入区域 -->
+     <script id="importTpl" type="text/template">
+         <form enctype="multipart/form-data" class="mt20 mb10">
+             <div class="col-xs-offset-1">
+                 <input type="hidden" id="prjId" name="prjId" th:value="${modelProject.id}">
+                 <input type="file" id="file" name="file"/>
+                 <div class="mt10 pt5">
+                     点击这里<a onclick="$.table.importTemplate()" class="btn btn-default btn-xs"><i class="fa fa-file-excel-o"></i> 下载模板</a>文件
+                 </div>
+                 <p class="pull-left mt10 text-danger">
+                     提示:仅允许导入“xls”或“xlsx”格式文件!
+                 </p>
+             </div>
+         </form>
+     </script>
     <script th:inline="javascript">
         var addFlag = [[${@permission.hasPermi('model:modelEntity:add')}]];
         var editFlag = [[${@permission.hasPermi('model:modelEntity:edit')}]];
@@ -86,6 +104,8 @@
                 updateUrl: prefix + "/edit/{id}",
                 removeUrl: prefix + "/remove",
                 exportUrl: prefix + "/export",
+                importUrl: prefix + "/importExpress",
+                importTemplateUrl: prefix + "/importTemplate",
                 modalName: "数据",
                 pagination: true,
                 showSearch: true,