Skip to content

整合 Enjoy 模版引擎 ​

本文介绍如何在 tio-boot-admin 项目中集成 Enjoy 模版引擎,并实现数据库数据渲染到前端页面。


一、引入 Enjoy 模版引擎 ​

在项目启动时,执行以下配置:

java
new TioAdminEnjoyEngineConfig().config();

该配置会完成:

  • 初始化 Enjoy 引擎
  • 设置模版路径
  • 启用模版渲染能力

二、后端实现 ​

1. Handler 编写 ​

创建一个 Handler,用于:

  • 查询数据库
  • 将数据注入模版
  • 返回 HTML 页面
java
package nexus.io.diamond.boradcast.handler;

import java.util.List;

import com.jfinal.kit.Kv;
import nexus.io.db.activerecord.Db;
import nexus.io.db.activerecord.Row;
import nexus.io.template.EnjoyEngine;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.tio.http.server.handler.HttpRequestHandler;
import nexus.io.tio.http.server.util.Resps;

public class IndexHanler implements HttpRequestHandler {

  @Override
  public HttpResponse handle(HttpRequest httpRequest) throws Exception {
    HttpResponse response = TioRequestContext.getResponse();

    String sql = "select id,title,description,audio_url,duration_sec,published_time "
        + "from db_episode where deleted=0 "
        + "order by date desc nulls last";

    List<Row> episodeList = Db.find(sql);

    String content = EnjoyEngine.renderToString(
        "/episode/index.html",
        Kv.by("episodeList", episodeList)
    );

    Resps.html(response, content);
    return response;
  }
}

2. SQL 说明(重要) ​

sql
order by date desc nulls last

作用:

  • 有 date 的记录优先
  • 按时间倒序排列
  • date = NULL 的数据排在最后

三、前端模版(Enjoy) ​

模版路径:

resources/episode/index.html

模版代码 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>剧集列表</title>
  <style>
    body {
      font-family: Arial, "Microsoft YaHei", sans-serif;
      background: #f7f7f7;
      padding: 20px;
      margin: 0;
    }

    .wrapper {
      max-width: 1200px;
      margin: 0 auto;
      background: #fff;
      padding: 20px;
      border-radius: 10px;
      box-shadow: 0 2px 10px rgba(0,0,0,.06);
    }

    h1 {
      margin-top: 0;
      font-size: 24px;
    }

    table {
      width: 100%;
      border-collapse: collapse;
      table-layout: fixed;
    }

    th, td {
      border: 1px solid #ebeef5;
      padding: 12px;
      text-align: left;
      vertical-align: top;
      word-break: break-word;
    }

    th {
      background: #f5f7fa;
    }

    .desc {
      white-space: pre-wrap;
      line-height: 1.6;
    }

    audio {
      width: 240px;
    }

    .empty {
      text-align: center;
      color: #999;
      padding: 30px 0;
    }
  </style>
</head>
<body>
<div class="wrapper">
  <h1>剧集列表</h1>

  #if(episodeList != null && episodeList.size() > 0)
  <table>
    <thead>
      <tr>
        <th style="width: 70px;">ID</th>
        <th style="width: 180px;">标题</th>
        <th>简介</th>
        <th style="width: 260px;">音频</th>
        <th style="width: 100px;">时长</th>
        <th style="width: 180px;">发布时间</th>
      </tr>
    </thead>
    <tbody>
      #for(ep : episodeList)
      <tr>
        <td>#(ep.id)</td>
        <td>#(ep.title)</td>
        <td class="desc">
          #if(ep.description != null && ep.description != "")
            #(ep.description)
          #else
            暂无简介
          #end
        </td>
        <td>
          #if(ep.audio_url != null && ep.audio_url != "")
            <audio controls preload="none">
              <source src="#(ep.audio_url)" type="audio/mpeg">
            </audio>
            <div style="margin-top:8px;">
              <a href="#(ep.audio_url)" target="_blank">打开音频</a>
            </div>
          #else
            无
          #end
        </td>
        <td>
          #if(ep.duration_sec != null)
            #(ep.duration_sec) 秒
          #else
            -
          #end
        </td>
        <td>#(ep.published_time)</td>
      </tr>
      #end
    </tbody>
  </table>
  #else
    <div class="empty">暂无数据</div>
  #end
</div>
</body>
</html>

四、Enjoy 模版语法说明 ​

1. 变量输出 ​

html
#(ep.title)

2. 条件判断 ​

html
#if(ep.description != null && ep.description != "")
  #(ep.description)
#else
  暂无简介
#end

3. 循环 ​

html
#for(ep : episodeList)
  ...
#end

五、数据流说明 ​

整体流程如下:

数据库 → Db.find → List<Row>
        ↓
Kv 注入数据
        ↓
Enjoy 模版渲染
        ↓
HTML 返回浏览器

六、最佳实践建议 ​

1. SQL 只查必要字段 ​

避免无用字段进入模版:

sql
select id,title,description,audio_url,duration_sec,published_time

2. 空值处理放在模版层 ​

例如:

  • 简介为空 → 显示“暂无简介”
  • 时长为空 → 显示“-”

3. 模版与业务分离 ​

  • SQL 写在后端
  • 页面逻辑写在 Enjoy
  • 不要在模版中写复杂业务逻辑

七、效果说明 ​

页面最终实现:

  • 列表展示剧集数据
  • 支持音频播放
  • 支持跳转查看音频资源
  • 自动处理空数据
  • 时间排序合理(空值在后)

八、总结 ​

通过以上步骤,你已经完成:

  • Enjoy 模版引擎接入
  • 后端数据查询
  • 模版渲染输出 HTML
  • 页面展示结构搭建

这套方式非常适合:

  • 管理后台页面
  • 内容展示页面
  • 轻量级服务端渲染场景

MIT Licensed