整合 Enjoy 模版引擎
本文介绍如何在 tio-boot-admin 项目中集成 Enjoy 模版引擎,并实现数据库数据渲染到前端页面。
一、引入 Enjoy 模版引擎
在项目启动时,执行以下配置:
new TioAdminEnjoyEngineConfig().config();
该配置会完成:
- 初始化 Enjoy 引擎
- 设置模版路径
- 启用模版渲染能力
二、后端实现
1. Handler 编写
创建一个 Handler,用于:
- 查询数据库
- 将数据注入模版
- 返回 HTML 页面
package com.litongjava.diamond.boradcast.handler;
import java.util.List;
import com.jfinal.kit.Kv;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Row;
import com.litongjava.template.EnjoyEngine;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.handler.HttpRequestHandler;
import com.litongjava.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 说明(重要)
order by date desc nulls last
作用:
- 有
date的记录优先 - 按时间倒序排列
date = NULL的数据排在最后
三、前端模版(Enjoy)
模版路径:
resources/episode/index.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. 变量输出
#(ep.title)
2. 条件判断
#if(ep.description != null && ep.description != "")
#(ep.description)
#else
暂无简介
#end
3. 循环
#for(ep : episodeList)
...
#end
五、数据流说明
整体流程如下:
数据库 → Db.find → List<Row>
↓
Kv 注入数据
↓
Enjoy 模版渲染
↓
HTML 返回浏览器
六、最佳实践建议
1. SQL 只查必要字段
避免无用字段进入模版:
select id,title,description,audio_url,duration_sec,published_time
2. 空值处理放在模版层
例如:
- 简介为空 → 显示“暂无简介”
- 时长为空 → 显示“-”
3. 模版与业务分离
- SQL 写在后端
- 页面逻辑写在 Enjoy
- 不要在模版中写复杂业务逻辑
七、效果说明
页面最终实现:
- 列表展示剧集数据
- 支持音频播放
- 支持跳转查看音频资源
- 自动处理空数据
- 时间排序合理(空值在后)
八、总结
通过以上步骤,你已经完成:
- Enjoy 模版引擎接入
- 后端数据查询
- 模版渲染输出 HTML
- 页面展示结构搭建
这套方式非常适合:
- 管理后台页面
- 内容展示页面
- 轻量级服务端渲染场景
