Ver Fonte

Merge remote-tracking branch 'origin/master'

luo.yibo@datuai.com há 1 ano atrás
pai
commit
42b8192033

+ 29 - 0
ruoyi-common/ruoyi-common-core/pom.xml

@@ -104,6 +104,35 @@
             <artifactId>transmittable-thread-local</artifactId>
         </dependency>
 
+        <!--pdf-->
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>html2pdf</artifactId>
+            <version>3.0.5</version>
+        </dependency>
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itextpdf</artifactId>
+            <version>5.5.13.2</version>
+        </dependency>
+        <!-- 中文字体支持 -->
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>font-asian</artifactId>
+            <version>7.1.17</version>
+        </dependency>
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.30</version>
+        </dependency>
+
+        <!-- 阿里JSON解析器 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 137 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/pdf/PdfUtil.java

@@ -0,0 +1,137 @@
+package org.dromara.common.core.utils.pdf;
+
+import com.itextpdf.html2pdf.ConverterProperties;
+import com.itextpdf.html2pdf.HtmlConverter;
+import com.itextpdf.io.font.PdfEncodings;
+import com.itextpdf.kernel.events.Event;
+import com.itextpdf.kernel.events.IEventHandler;
+import com.itextpdf.kernel.events.PdfDocumentEvent;
+import com.itextpdf.kernel.font.PdfFont;
+import com.itextpdf.kernel.font.PdfFontFactory;
+import com.itextpdf.kernel.geom.PageSize;
+import com.itextpdf.kernel.pdf.PdfDocument;
+import com.itextpdf.kernel.pdf.PdfWriter;
+import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
+import com.itextpdf.layout.Document;
+import com.itextpdf.layout.font.FontProvider;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+public class PdfUtil {
+
+    public static final String DEFAULT_FONTS_SIMSUN_TTC = "/fonts/simsun.ttc,0";
+
+    private static Configuration freemarkerCfg = null;
+
+    public static final String DOCTEMPLATES_PATH = "/template/ftl";
+
+    static {
+        freemarkerCfg = new Configuration(Configuration.VERSION_2_3_20);
+        //freemarker的模板目录
+        freemarkerCfg.setClassForTemplateLoading(PdfUtil.class, DOCTEMPLATES_PATH);
+
+    }
+
+    /**
+     * freemarker渲染html
+     * @param data  数据,可以是任何对象,会传入freemaker上下文,按照对应预发渲染
+     * @param htmlTmp  html模板
+     * @param os 渲染输出
+     */
+    public static void freeMarkerRender(Object data, String htmlTmp, OutputStream os) {
+        try (Writer out = new OutputStreamWriter(os)) {
+            // 获取模板,并设置编码方式
+            Template template = freemarkerCfg.getTemplate(htmlTmp);
+            // 合并数据模型与模板
+            //将合并后的数据和模板写入到流中,这里使用的字符流
+            template.process(data, out);
+            out.flush();
+        } catch (Exception e) {
+            log.error("渲染pdf失败:{}", e.getMessage());
+
+        }
+    }
+
+    private PdfUtil() {
+    }
+
+    /**
+     * html转pdf
+     *
+     * @param inputStream  输入内容
+     * @param waterMark    水印
+     * @param fontPath     字体路径,ttc后缀的字体需要添加<b>,0<b/>
+     * @param outputStream 输出流
+     * @date : 2022/01/22 14:07
+     */
+    public static void convertHtmlToPdf(InputStream inputStream, String waterMark, String fontPath, OutputStream outputStream, PageSize pageSize) throws IOException {
+        PdfWriter pdfWriter = new PdfWriter(outputStream);
+        PdfDocument pdfDocument = new PdfDocument(pdfWriter);
+        //设置为A4大小
+        pdfDocument.setDefaultPageSize(PageSize.A4);
+        if(pageSize != null){
+            pdfDocument.setDefaultPageSize(pageSize);
+        }
+
+        //添加水印
+        pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, new WaterMarkEventHandler(waterMark));
+
+        //添加中文字体支持
+        ConverterProperties properties = new ConverterProperties();
+        FontProvider fontProvider = new FontProvider();
+        //添加引入的字体文件
+        PdfFont font = PdfFontFactory.createFont(DEFAULT_FONTS_SIMSUN_TTC);
+        fontProvider.addFont(font.getFontProgram());
+        //添加自定义字体,例如微软雅黑
+        if (StringUtils.isNotBlank(fontPath)) {
+            PdfFont microsoft = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED);
+            fontProvider.addFont(microsoft.getFontProgram(), PdfEncodings.IDENTITY_H);
+        }
+        properties.setFontProvider(fontProvider);
+
+
+        HtmlConverter.convertToPdf(inputStream, pdfDocument, properties);
+        pdfWriter.close();
+        pdfDocument.close();
+    }
+
+
+    /**
+     * 按照模板渲染pdf文档
+     *
+     * @param templatePath 文档模板,参考文档模板目录
+     * @param data         要渲染的动态数据
+     */
+    public static void renderPdf(String templatePath, Object data, OutputStream outputStream,PageSize pageSize) {
+
+        log.debug("pdf template render,template:{},data:{}", templatePath, data);
+        try (ByteArrayOutputStream osBuf = new ByteArrayOutputStream()) {
+            // freemarker 渲染,结果存放到字节流
+            freeMarkerRender(data, templatePath, osBuf);
+            ByteArrayInputStream isBuf = new ByteArrayInputStream(osBuf.toByteArray());
+
+            // 两个empty参数可以扩展,支持水印和自定义字体
+            convertHtmlToPdf(isBuf, "", "", outputStream,pageSize);
+            outputStream.flush();
+        } catch (IOException e) {
+            log.info("pdf template render error:{}", e.getMessage());
+        }
+
+    }
+
+    public static void main(String[] args) throws FileNotFoundException {
+        Map<String,String> hashMap = new HashMap<>();
+        hashMap.put("currentDate", "dsfsadfsddasfd");
+        PdfUtil.renderPdf("bussinessTemplate.html",hashMap, new FileOutputStream("D:/temp1.pdf"),PageSize.A3);
+        System.err.println("ok");
+    }
+
+
+}

+ 14 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/pdf/WaterMarkEventHandler.java

@@ -0,0 +1,14 @@
+package org.dromara.common.core.utils.pdf;
+
+import com.itextpdf.kernel.events.Event;
+import com.itextpdf.kernel.events.IEventHandler;
+
+public class WaterMarkEventHandler implements IEventHandler {
+    public WaterMarkEventHandler(String waterMark) {
+    }
+
+    @Override
+    public void handleEvent(Event event) {
+
+    }
+}

BIN
ruoyi-common/ruoyi-common-core/src/main/resources/fonts/simsun.ttc


+ 13 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/controller/PtAccountController.java

@@ -1,11 +1,17 @@
 package org.dromara.backstage.basics.controller;
 
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
+import com.itextpdf.kernel.geom.PageSize;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.common.core.utils.pdf.PdfUtil;
 import org.dromara.common.message.kafka.aop.annotation.SyncDataToLocal;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
@@ -109,4 +115,11 @@ public class PtAccountController extends BaseController {
                           @PathVariable Long[] accountIds) {
         return toAjax(ptAccountService.deleteWithValidByIds(List.of(accountIds), true));
     }
+
+    /*public static void main(String[] args) throws FileNotFoundException {
+        Map<String,String> hashMap = new HashMap<>();
+        hashMap.put("currentDate", "11111");
+        PdfUtil.renderPdf("bussinessTemplate1.html",hashMap, new FileOutputStream("D:/temp1.pdf"), PageSize.A3);
+        System.err.println("ok");
+    }*/
 }

+ 57 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/cardCenter/controller/PtSubsidyitemController.java

@@ -1,14 +1,27 @@
 package org.dromara.backstage.cardCenter.controller;
 
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 
+import com.itextpdf.kernel.geom.PageSize;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import org.dromara.backstage.cardCenter.domain.bo.PtSubsidyReportBo;
 import org.dromara.backstage.cardCenter.domain.vo.PtSubsidyReportVo;
+import org.dromara.common.core.utils.DateUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.pdf.PdfUtil;
 import org.dromara.common.message.kafka.aop.annotation.SyncDataToLocal;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.system.api.model.LoginUser;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -127,4 +140,48 @@ public class PtSubsidyitemController extends BaseController {
         List<PtSubsidyReportVo> list = ptSubsidyitemService.queryReportList(bo);
         ExcelUtil.exportExcel(list, "补助报表", PtSubsidyReportVo.class, response);
     }
+
+    @SaCheckPermission("subsidy:ptSubsidyitem:ptSubsidyitemReport")
+    @Log(title = "补助报表pdf导出", businessType = BusinessType.EXPORT)
+    @PostMapping("/exportPDFReport")
+    public void exportPDFReport(PtSubsidyReportBo bo, HttpServletResponse response) {
+        List<PtSubsidyReportVo> list = ptSubsidyitemService.queryReportList(bo);
+        // 使用单位
+        LoginUser loginUser = Optional.ofNullable(LoginHelper.getLoginUser()).orElse(new LoginUser());
+        String useUint = loginUser.getDeptName();
+        // 制表人
+        String makeMan = loginUser.getUsername();
+        // 制表时间
+        String makeTime = DateUtils.getTime();
+        String queryTime = "至今";
+        // 查询时间范围
+        if(bo.getBeginCountTime()!=null && bo.getEndCountTime() !=null){
+            queryTime = DateUtils.dateTime(bo.getBeginCountTime()) + "至" + DateUtils.dateTime(bo.getEndCountTime());
+        }
+        String fileName = "补助统计报表" + DateUtils.dateTimeNow() + ".pdf";
+        Map<String, Object> hashMap = new HashMap<>();
+        hashMap.put("useUint", useUint);
+        hashMap.put("makeMan", makeMan);
+        hashMap.put("makeTime", makeTime);
+        hashMap.put("queryTime", queryTime);
+        hashMap.put("itemList", list);
+        // 合计金额
+        hashMap.put("totalAmount", list.stream().map(PtSubsidyReportVo::getReceiveMoney).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
+        // 合计笔数
+        hashMap.put("totalCount", list.stream().map(PtSubsidyReportVo::getReceiveNumber).reduce(Long::sum).orElse(0L));
+
+        try {
+            response.reset();
+            response.addHeader("Access-Control-Allow-Origin", "*");
+            response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
+            //response.addHeader("Content-Length", "" + data.length);
+            response.setContentType("application/pdf; charset=UTF-8");
+            response.setHeader("Content-Disposition", URLEncoder.encode(fileName, StandardCharsets.UTF_8));
+
+            PdfUtil.renderPdf("PtSubsidyReport.html",hashMap, response.getOutputStream(), PageSize.A4);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }

+ 1 - 1
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/cardCenter/domain/vo/PtSubsidyReportVo.java

@@ -39,7 +39,7 @@ public class PtSubsidyReportVo implements Serializable {
      * 补助类型
      */
     @ExcelProperty(value = "补助类型", converter = ExcelDictConvert.class)
-    @ExcelDictFormat(dictType = "SUBTYPE")
+    @ExcelDictFormat(readConverterExp = "1=教师补助,2=学员补助,所有=所有")
     private String subsidyType;
 
     /**

+ 66 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/consumption/controller/XfConsumeDetailController.java

@@ -1,8 +1,14 @@
 package org.dromara.backstage.consumption.controller;
 
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
+import com.itextpdf.kernel.geom.PageSize;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
@@ -11,7 +17,10 @@ import org.dromara.backstage.consumption.domain.bo.XfConsumeDetailBo;
 import org.dromara.backstage.consumption.domain.vo.XfConsumeAnalyzeVo;
 import org.dromara.backstage.consumption.domain.vo.XfConsumeDetailCKBKVo;
 import org.dromara.backstage.consumption.domain.vo.XfConsumeDetailVo;
+import org.dromara.common.core.utils.DateUtils;
+import org.dromara.common.core.utils.pdf.PdfUtil;
 import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.system.api.model.LoginUser;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -141,6 +150,7 @@ public class XfConsumeDetailController extends BaseController {
     }
 
 
+    @SaCheckPermission("consumption:xfConsumedetail:export")
     @Log(title = "营业报表导出", businessType = BusinessType.EXPORT)
     @PostMapping("/exportConsumeAnalyze")
     public void exportConsumeAnalyze(PageQuery pageQuery, String dateFormat, boolean groupByTerm,String accountIds
@@ -151,4 +161,60 @@ public class XfConsumeDetailController extends BaseController {
             endDate).getRows();
         ExcelUtil.exportExcel(list, "营业报表", XfConsumeAnalyzeVo.class, response);
     }
+
+    @SaCheckPermission("consumption:xfConsumedetail:export")
+    @Log(title = "营业报表PDF导出", businessType = BusinessType.EXPORT)
+    @PostMapping("/exportPDFConsumeAnalyze")
+    public void exportPDFConsumeAnalyze(PageQuery pageQuery, String dateFormat, boolean groupByTerm,String accountIds
+        ,String roomIds,String beginDate, String endDate, HttpServletResponse response) {
+        pageQuery.setPageSize(100000);
+        List<XfConsumeAnalyzeVo> list = xfConsumeDetailService.consumeAnalyze(pageQuery,dateFormat, groupByTerm, accountIds, roomIds
+            , beginDate,
+            endDate).getRows();
+        // 使用单位
+        LoginUser loginUser = Optional.ofNullable(LoginHelper.getLoginUser()).orElse(new LoginUser());
+        String useUint = loginUser.getDeptName();
+        // 制表人
+        String makeMan = loginUser.getUsername();
+        // 制表时间
+        String makeTime = DateUtils.getTime();
+        // 查询时间范围
+        String queryTime = beginDate + "至" + endDate;
+        String fileName = "_营业报表_" + DateUtils.dateTimeNow() + ".pdf";
+        if(DateUtils.YYYY.equals(dateFormat)){
+            // 查询年份的最后一天
+            queryTime = beginDate +" 01-01" + "~" + DateUtils.lastDayOfYear(Integer.parseInt(endDate));
+            fileName = endDate + "年"+ fileName;
+        }else if(DateUtils.YYYY_MM.equals(dateFormat)){
+            String year = endDate.split("-")[0];
+            String month = endDate.split("-")[1];
+            queryTime = beginDate +"-01" + "~" + DateUtils.lastDayOfMonth(Integer.parseInt(year), Integer.parseInt(month));
+            fileName = endDate + "年"+ month+"月" +fileName;
+        }
+        Map<String, Object> hashMap = new HashMap<>();
+        hashMap.put("useUint", useUint);
+        hashMap.put("makeMan", makeMan);
+        hashMap.put("makeTime", makeTime);
+        hashMap.put("queryTime", queryTime);
+        hashMap.put("itemList", list);
+        // 合计金额
+        hashMap.put("totalAmount", list.stream().mapToDouble(e -> Double.parseDouble(e.getXiaoJiJinE())).sum());
+        // 合计笔数
+        hashMap.put("totalCount", list.stream().mapToInt(e -> Integer.parseInt(e.getXiaoJiCiShu())).sum());
+
+        try {
+            response.reset();
+            response.addHeader("Access-Control-Allow-Origin", "*");
+            response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
+            //response.addHeader("Content-Length", "" + data.length);
+            response.setContentType("application/pdf; charset=UTF-8");
+            response.setHeader("Content-Disposition", URLEncoder.encode(fileName, StandardCharsets.UTF_8));
+
+            PdfUtil.renderPdf("bussinessTemplate1.html",hashMap, response.getOutputStream(), PageSize.A4);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
 }

+ 70 - 0
ruoyi-modules/ruoyi-backstage/src/main/resources/template/ftl/PtSubsidyReport.html

@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>补助统计报表</title>
+</head>
+<body>
+<h3 style="text-align: center;font-size: 14px"><b>补助统计报表</b></h3>
+<div style="margin: 0 auto;">
+    <div style="width:100%;">
+        <div class="item-space">
+            <div>使用单位:${useUint!}</div>
+            <div>制表时间:${makeTime!}</div>
+        </div>
+        <div class="item-space">
+            <div>制表人:${makeMan!}</div>
+            <div>查询时间范围:${queryTime!}</div>
+        </div>
+    </div>
+    <table style="border: 1px solid #000;border-collapse:collapse;width: 100%;">
+        <thead>
+        <tr>
+            <th>日期</th>
+            <th>补助类型</th>
+            <th>补助总金额(元)</th>
+            <th>笔数</th>
+            <th>已领金额(元)</th>
+            <th>已领笔数</th>
+            <th>未领金额</th>
+            <th>未领笔数</th>
+            <th>创建者</th>
+        </tr>
+        </thead>
+        <tbody>
+        <#list (itemList)! as item>
+        <tr>
+            <td>${(item.fillDate)!}</td>
+            <td>${(item.subsidyType)!}</td>
+            <td>${(item.fillMoney)!}</td>
+            <td>${(item.countNumber)!}</td>
+            <td>${(item.receiveMoney)!}</td>
+            <td>${(item.receiveNumber)!}</td>
+            <td>${(item.notReceiveMoney)!}</td>
+            <td>${(item.notReceiveNumber)!}</td>
+            <td>${(item.createByName)!}</td>
+        </tr>
+        </#list>
+        <tr style="text-align: center;">
+            <td colspan="15"><span>已领取合计:${(totalAmount)!}</span>  <span style="margin-left: 10px;">已领取笔数合计:${(totalCount)!}</span></td>
+        </tr>
+        </tbody>
+    </table>
+</div>
+</body>
+<style>
+    body {
+        font-size: 8px;
+    }
+    td,th {
+        border: 1px solid #000;
+        text-align: center;
+    }
+    .item-space {
+        margin-left : 20px;
+        margin-right: 20px;
+        display: inline-block;
+    }
+</style>
+</html>

+ 83 - 0
ruoyi-modules/ruoyi-backstage/src/main/resources/template/ftl/bussinessTemplate1.html

@@ -0,0 +1,83 @@
+
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>营业报表</title>
+</head>
+<body>
+<h3 style="text-align: center;font-size: 14px"><b>营业报表</b></h3>
+<div style="margin: 0 auto;">
+    <div style="width:100%;">
+        <div class="item-space">
+            <div>使用单位:${useUint!}</div>
+            <div>制表时间:${makeTime!}</div>
+        </div>
+        <div class="item-space">
+            <div>制表人:${makeMan!}</div>
+            <div>查询时间范围:${queryTime!}</div>
+        </div>
+    </div>
+    <table style="border: 1px solid #000;border-collapse:collapse;width: 100%;">
+        <thead>
+        <tr>
+            <th>日期</th>
+            <th>机器名称</th>
+            <th>结算账户</th>
+            <th>早餐金额</th>
+            <th>早餐笔数</th>
+            <th>午餐金额</th>
+            <th>午餐笔数</th>
+            <th>晚餐金额</th>
+            <th>晚餐笔数</th>
+            <th>夜宵金额</th>
+            <th>夜宵笔数</th>
+            <th>错扣补款金额</th>
+            <th>错扣补款笔数</th>
+            <th>金额小计</th>
+            <th>笔数小计</th>
+        </tr>
+        </thead>
+        <tbody>
+        <#list (itemList)! as item>
+            <tr>
+                <td>${(item.consumeDate)!}</td>
+                <td>${(item.termName)!}</td>
+                <td>${(item.accountName)!}</td>
+                <td>${(item.zaoCanJinE)!}</td>
+                <td>${(item.zaoCanCiShu)!}</td>
+                <td>${(item.wuCanJinE)!}</td>
+                <td>${(item.wuCanCiShu)!}</td>
+                <td>${(item.wanCanJinE)!}</td>
+                <td>${(item.wanCanCiShu)!}</td>
+                <td>${(item.xiaoYeJinE)!}</td>
+                <td>${(item.xiaoYeCiShu)!}</td>
+                <td>${(item.tuiKuanJinE)!}</td>
+                <td>${(item.tuiKuanCiShu)!}</td>
+                <td>${(item.xiaoJiJinE)!}</td>
+                <td>${(item.xiaoJiCiShu)!}</td>
+            </tr>
+        </#list>
+            <tr style="text-align: center;">
+                <td colspan="15"><span>金额合计:${(totalAmount)!}</span>  <span style="margin-left: 10px;">笔数合计:${(totalCount)!}</span></td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+</body>
+<style>
+    body {
+        font-size: 8px;
+    }
+    td,th {
+        border: 1px solid #000;
+        text-align: center;
+    }
+    .item-space {
+        margin-left : 20px;
+        margin-right: 20px;
+        display: inline-block;
+    }
+</style>
+</html>