在小说类Laravel项目开发中,点击小说封面图片弹出模态框展示对应剧集列表是提升用户交互体验的常见功能,实现过程需要前后端配合完成数据传递与页面渲染。
功能实现整体流程
整个功能可以分为三个核心部分:前端页面图片点击事件处理、后端剧集数据接口开发、模态框动态内容渲染。下面逐步拆解每个部分的实现细节。
1. 数据库表结构准备
首先需要准备小说表和剧集表,两者通过小说ID关联,表结构参考如下:
| 表名 | 核心字段 | 说明 |
|---|---|---|
| novels | id, title, cover_image | 存储小说基础信息,cover_image为封面图片路径 |
| novel_episodes | id, novel_id, episode_num, title, content | 存储小说剧集信息,novel_id关联对应小说 |
2. 后端路由与控制器实现
首先定义获取小说剧集的接口路由,在routes/web.php中添加如下代码:
Route::get('/novel/{novel}/episodes', [NovelController::class, 'getEpisodes'])->name('novel.episodes');
然后在app/Http/Controllers/NovelController.php中编写对应的控制器方法:
<?php
namespace AppHttpControllers;
use AppModelsNovel;
use IlluminateHttpRequest;
class NovelController extends Controller
{
/**
* 获取指定小说的所有剧集
* @param Novel $novel 路由模型绑定的小说实例
* @return IlluminateHttpJsonResponse
*/
public function getEpisodes(Novel $novel)
{
// 查询该小说下所有剧集,按剧集号升序排列
$episodes = $novel->episodes()->orderBy('episode_num')->get();
return response()->json([
'code' => 0,
'msg' => 'success',
'data' => $episodes
]);
}
}
3. 前端页面图片与模态框结构
在小说列表页面,先展示小说封面图片,同时给图片绑定点击事件,传递小说ID参数:
<div class="novel-list">
<!-- 假设$novel是当前遍历的小说对象 -->
<div class="novel-item">
<img
src="{{ $novel->cover_image }}"
class="novel-cover"
data-novel-id="{{ $novel->id }}"
alt="{{ $novel->title }}封面"
>
<p class="novel-title">{{ $novel->title }}</p>
</div>
</div>
<!-- 模态框结构,默认隐藏 -->
<div class="modal" id="episodeModal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<span class="modal-title"></span>
<span class="modal-close">×</span>
</div>
<div class="modal-body">
<div class="episode-loading">加载中...</div>
<ul class="episode-list"></ul>
</div>
</div>
</div>
4. JavaScript交互逻辑实现
使用jQuery处理图片点击事件,调用后端接口获取数据后渲染到模态框中:
$(function() {
// 点击小说封面图片触发事件
$('.novel-cover').on('click', function() {
const novelId = $(this).data('novel-id');
const novelTitle = $(this).siblings('.novel-title').text();
// 显示模态框,设置标题
$('#episodeModal').show();
$('.modal-title').text(novelTitle + ' - 剧集列表');
// 显示加载状态,清空之前的剧集列表
$('.episode-loading').show();
$('.episode-list').empty();
// 调用后端接口获取剧集数据
$.ajax({
url: '/novel/' + novelId + '/episodes',
type: 'GET',
dataType: 'json',
success: function(res) {
$('.episode-loading').hide();
if (res.code === 0 && res.data.length > 0) {
// 遍历剧集数据生成列表项
$.each(res.data, function(index, episode) {
const li = '<li>第' + episode.episode_num + '集:' + episode.title + '</li>';
$('.episode-list').append(li);
});
} else {
$('.episode-list').append('<li>暂无剧集内容</li>');
}
},
error: function() {
$('.episode-loading').hide();
$('.episode-list').append('<li>获取剧集失败,请重试</li>');
}
});
});
// 点击关闭按钮隐藏模态框
$('.modal-close').on('click', function() {
$('#episodeModal').hide();
});
// 点击模态框外部区域也隐藏模态框
$(window).on('click', function(e) {
if ($(e.target).is('#episodeModal')) {
$('#episodeModal').hide();
}
});
});
5. 基础样式适配
给模态框添加基础样式,保证显示效果正常:
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
z-index: 1000;
}
.modal-content {
width: 600px;
margin: 100px auto;
background-color: #fff;
border-radius: 8px;
overflow: hidden;
}
.modal-header {
padding: 15px 20px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-close {
cursor: pointer;
font-size: 24px;
}
.modal-body {
padding: 20px;
max-height: 400px;
overflow-y: auto;
}
.episode-list li {
padding: 10px 0;
border-bottom: 1px dashed #eee;
cursor: pointer;
}
.episode-list li:hover {
color: #1890ff;
}
.episode-loading {
text-align: center;
color: #999;
padding: 20px 0;
}
注意事项
- 后端接口需要做权限校验,避免未授权用户访问非公开小说的剧集数据
- 如果剧集数量较多,可以在后端接口中添加分页逻辑,前端滚动加载更多
- 图片路径需要保证正确,若使用云存储可替换为对应的CDN地址
- 模态框样式可以根据项目的UI规范调整,上述仅为基础示例