# 一、数据库数据准备
通过模版页面分析网站所需要的分类,并上传测试或者真实数据用于页面展示【具体看课程视频111课】
# 二、读取数据库数据渲染到模版页面(以合作单位为例)
- 控制器
app/controller/api/template01/api.js
async index() {
const { ctx,app } = this;
// await ctx.render('api/template01/main_app.html',{
// title:'网站首页',
// tempType: 'index',
// });
//合作单位
const cooperation_unit = await ctx.model.News.findAll({
where:{
category_id: 10,
status: 1,
},
order: [
['order', 'asc'],
['id', 'DESC'],
],
limit: 4,
// 关联查询
include: [
{
model: app.model.Category,// 需要查询的模型
attributes: ['id', 'name', 'enname'],// 查询的字段
}
],
});
console.log('合作单位',JSON.parse(JSON.stringify(cooperation_unit)));
let arr = [];
arr.push({
type:'cooperation_unit',
sqldata:cooperation_unit
});
await ctx.renderApi({
title:'网站首页',
tempType: 'index',
data:arr
},'api/template01/main_app.html');
}
- 扩展
app/extend/context.js
//前端模版渲染
/**
*
* @param {Object} params - 用于渲染模板的数据对象。应包含模板所需的所有键值对
* @param {string} tplname - [tplname='api/template01/main_app.html'] - 模板文件的路径。默认为内置的基础API模板路径,可不填。
* @returns {Promise<void>} - 返回一个Promise,表示渲染操作的完成状态。无具体返回值。
* @throws {Error} - 如果模板加载或渲染过程中发生错误,可能会抛出异常。
*/
async renderApi(params, tplname = 'api/template01/main_app.html') {
await this.render(tplname,params);
},
- 模版
app/view/api/template01/_index.html
{% for item in data %}
{% if item.type == 'cooperation_unit' and item.sqldata.length > 0 %}
<!-- 合作单位 -->
<div id="cooperation_unit" class="flex justify-center py-5">
<div style="width: 1200px;">
<!-- 栏目名称 -->
<div>
<!-- 圆角 -->
<div class="flex justify-center">
<div class="div-rounded"></div>
</div>
<!-- 英文 -->
<div class="flex justify-center mt-2">
<span class="span-en">{{item.sqldata[0].category.enname}}</span>
</div>
<!-- 中文 -->
<div class="flex justify-center">
<span class="span-ch">{{item.sqldata[0].category.name}}</span>
</div>
</div>
<!-- 栏目内容 -->
<div class="flex justify-between mt-5">
{# <img src="/public/api/template01/static/image/unit/unit01.jpg" >
<img src="/public/api/template01/static/image/unit/unit02.jpg" >
<img src="/public/api/template01/static/image/unit/unit03.jpg" >
<img src="/public/api/template01/static/image/unit/unit04.jpg" > #}
{% for item1 in item.sqldata %}
<img src="{{item1.attachment}}" >
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% endfor %}
# 三、封装通过分类id获取数据的方法到服务中,简化读取数据操作(以资讯中心为例)
- 控制器
app/controller/api/template01/api.js
async index() {
const { ctx,app } = this;
//合作单位
const cooperation_unit = await ctx.service.news.getNewsByCategoryId(10,4);
console.log('合作单位',JSON.parse(JSON.stringify(cooperation_unit)));
//资讯中心(新闻中心)
const news_center = await ctx.service.news.getNewsByCategoryId(6,3);
console.log('资讯中心(新闻中心)',JSON.parse(JSON.stringify(news_center)));
let arr = [];
arr.push({type:'news_center',sqldata:news_center});
arr.push({type:'cooperation_unit',sqldata:cooperation_unit});
await ctx.renderApi({
title:'网站首页',
tempType:'index',
data:arr
},'api/template01/main_app.html');
}
- 新建服务
app/service/news.js
'use strict';
const Service = require('egg').Service;
class NewsService extends Service {
// 根据分类id获取新闻信息
/**
*
* @param {number} category_id - 分类id,必填
* @param {number} limit - 取多少条数据,默认:4 ,可选
* @param {object} options - 排序规则,可选
* @returns {Promise<object>} - 返回一个Promise,返回值是数据对象
*/
async getNewsByCategoryId(category_id,limit = 4, options = {}){
const { ctx, app } = this;
//如果没有传排序规则
if (!options.order) {
options.order = [
['order','asc'],
['id','desc']
]
}
const res = await ctx.model.News.findAll({
where:{
status:1,
category_id
},
limit,
...options,
//关联查询
include: [
{
model: app.model.Category,// 需要查询的模型
attributes: ['id', 'name', 'enname'],// 查询的字段
}
],
});
return res;
}
}
module.exports = NewsService;
- 模版
app/view/api/template01/_index.html
...
{% if item.type == 'news_center' and item.sqldata.length > 0 %}
...
<!-- 图片 -->
<div>
<img
src="{{item.sqldata[0].attachment if item.sqldata[0].attachment else '/public/api/template01/static/image/newspic01.jpg'}}"
style="width: 560px;height: 360px;">
</div>
<!-- 列表 -->
<div class="news-list flex justify-between flex-column" style="width: 575px;height: 360px;">
{% for item1 in item.sqldata %}
<div class="flex">
<!-- 日期 -->
<div style="width: 80px;flex-shrink: 0;" class="flex justify-start flex-column">
<strong>{{item1.create_time.split(' ')[0].substring(8)}}</strong>
<span>{{item1.create_time.split(' ')[0].substr(0,7).replace('-','/')}}</span>
</div>
<!-- 内容 -->
<div style="flex: auto;width: 0;">
<!-- 标题 -->
<h3 class="mt-0 lines-1">{{item1.name}}</h3>
<!-- 描述 -->
<p class="lines-2 text-light-muted">{{item1.description}}</p>
</div>
</div>
{% endfor %}
</div>
...
{% endif %}
...
# 四、完善首页其它板块的数据展示
- 控制器
app/controller/api/template01/api.js
async index() {
const { ctx,app } = this;
let arr = [];
//banner广告图
arr.push({type:'banner',sqldata:await ctx.service.news.getNewsByCategoryId(11,1)});
//主营业务
arr.push({type:'main_business',sqldata:await ctx.service.news.getNewsByCategoryId(13,3)});
//关于我们
arr.push({type:'about_us',sqldata:await ctx.service.news.getNewsByCategoryId(5,1)});
//工程案例
arr.push({type:'engineering_case',sqldata:await ctx.service.news.getNewsByCategoryId(8,8)});
//资讯中心(新闻中心)
arr.push({type:'news_center',sqldata:await ctx.service.news.getNewsByCategoryId(6,3)});
//合作单位
arr.push({type:'cooperation_unit',sqldata:await ctx.service.news.getNewsByCategoryId(10,4)});
// await ctx.render('api/template01/main_app.html',{
// title:'网站首页',
// tempType:'index',
// data:arr
// });
await ctx.renderApi({
title:'网站首页',
tempType:'index',
data:arr
},'api/template01/main_app.html');
}
- 模版
app/view/api/template01/_index.html
{% for item in data %}
{% if item.type == 'banner' and item.sqldata.length > 0 %}
<!-- banner广告图 -->
<div id="banner" class="flex justify-center position-relative" style="margin-top: 100px;">
<div style="width: 100%;">
<!-- 图片 -->
<div>
<img src="{{item.sqldata[0].attachment if item.sqldata[0].attachment else '/public/api/template01/static/image/banner.jpg'}}" style="width: 100%;height: auto;" />
</div>
<!-- 按钮部分 -->
<div class="banner-btn flex justify-center position-absolute left-0 right-0 bottom-0" style="height: 50px;">
<div class="active"></div>
<div></div>
<div></div>
</div>
</div>
</div>
{% endif %}
{% if item.type == 'main_business' and item.sqldata.length > 0 %}
<!-- 主营业务 -->
<div id="main_business" class="flex justify-center py-5">
<div style="width: 1200px;">
<!-- 业务名称 -->
<div>
<!-- 圆角 -->
<div class="flex justify-center">
<div class="div-rounded"></div>
</div>
<!-- 英文 -->
<div class="flex justify-center mt-2">
<span class="span-en">MAIN BUSINESS</span>
</div>
<!-- 中文 -->
<div class="flex justify-center">
<span class="span-ch">主营业务</span>
</div>
</div>
<!-- 业务内容 -->
<div class="flex justify-between mt-5">
{% for item1 in item.sqldata %}
<div>
<!-- 图片 -->
<div>
<img src="{{item1.attachment}}" alt="{{item1.name}}">
</div>
<!-- 业务描述 -->
<div>
<!-- 业务名称 -->
<h3>{{item1.name}}</h3>
<!-- 描述 -->
<p>{{item1.description}}</p>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% if item.type == 'about_us' and item.sqldata.length > 0 %}
<!-- 关于我们 -->
<div id="about_us" class="flex justify-center">
<div style="width: 1200px;margin-top: 100px;">
<!-- 栏目名称 -->
<div>
<!-- 英文 -->
<div>
<span class="span-en">{{item.sqldata[0].category.enname}}</span>
</div>
<!-- 中文 -->
<div>
<span class="span-ch">{{item.sqldata[0].category.name}}</span>
</div>
</div>
<!-- 公司简介 -->
<div class="introduction flex justify-between">
<!-- 简介部分 -->
<div>
<!-- 文字部分 -->
<div>
<!-- 文字 -->
<p>
{{item.sqldata[0].description}}
</p>
</div>
<!-- 按钮部分 -->
<div style="width: 640px;height: 60px;">
<a href="./detail.html" target="_blank">MORE</a>
</div>
</div>
<!-- 图片 -->
<div>
<img src="{{item.sqldata[0].attachment if item.sqldata[0].attachment else '/public/api/template01/static/image/company-bg.jpg'}}">
</div>
</div>
<!-- 公司实力 -->
<div style="margin-top: 80px;height: 100px;" class="strength flex justify-between">
<div>
<strong>2012年</strong>
<span>公司成立于</span>
</div>
<div>
<strong>5000万</strong>
<span>注册资本</span>
</div>
<div>
<strong>100+</strong>
<span>在职员工</span>
</div>
<div>
<strong>3000+</strong>
<span>累积积累客户</span>
</div>
</div>
</div>
</div>
{% endif %}
{% if item.type == 'engineering_case' and item.sqldata.length > 0 %}
<!-- 工程案例 -->
<div id="engineering_case" class="flex justify-center py-5">
<div style="width: 1200px;">
<!-- 案例名称 -->
<div>
<!-- 圆角 -->
<div class="flex justify-center">
<div class="div-rounded"></div>
</div>
<!-- 英文 -->
<div class="flex justify-center mt-2">
<span class="span-en">{{item.sqldata[0].category.enname}}</span>
</div>
<!-- 中文 -->
<div class="flex justify-center">
<span class="span-ch">{{item.sqldata[0].category.name}}</span>
</div>
</div>
<!-- 案例内容 -->
<div class="case-div flex mt-5" style="flex-wrap: wrap;">
{% for item1 in item.sqldata %}
<div class="list">
<!-- 图片 -->
<div>
<img src="{{item1.attachment}}">
</div>
<!-- 标题 -->
<div class="text-center mt-2">
<a href="#" target="_blank" class="text-no-underline text-dark lines-1 display-block"
title="{{item1.name}}">{{item1.name}}</a>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% if item.type == 'news_center' and item.sqldata.length > 0 %}
<!-- 新闻中心 -->
<div id="news_center" class="flex justify-center py-5">
<div style="width: 1200px;">
<!-- 新闻名称 -->
<div>
<!-- 圆角 -->
<div class="flex justify-center">
<div class="div-rounded"></div>
</div>
<!-- 英文 -->
<div class="flex justify-center mt-2">
<span class="span-en">{{item.sqldata[0].category.enname}}</span>
</div>
<!-- 中文 -->
<div class="flex justify-center">
<span class="span-ch">{{item.sqldata[0].category.name}}</span>
</div>
</div>
<!-- 新闻内容 -->
<div class="flex justify-between mt-5">
<!-- 图片 -->
<div>
<img src="{{item.sqldata[0].attachment if item.sqldata[0].attachment else '/public/api/template01/static/image/newspic01.jpg'}}"
style="width: 560px;height: 360px;">
</div>
<!-- 列表 -->
<div class="news-list flex justify-between flex-column" style="width: 575px;height: 360px;">
{% for item1 in item.sqldata %}
<div class="flex">
<!-- 日期 -->
<div style="width: 80px;flex-shrink: 0;" class="flex justify-start flex-column">
<strong>{{item1.create_time.split(' ')[0].substring(8)}}</strong>
<span>{{item1.create_time.split(' ')[0].substr(0,7).replace('-','/')}}</span>
</div>
<!-- 内容 -->
<div style="flex: auto;width: 0;">
<!-- 标题 -->
<h3 class="mt-0 lines-1">{{item1.name}}</h3>
<!-- 描述 -->
<p class="lines-2 text-light-muted">{{item1.description}}</p>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
{% if item.type == 'cooperation_unit' and item.sqldata.length > 0 %}
<!-- 合作单位 -->
<div id="cooperation_unit" class="flex justify-center py-5">
<div style="width: 1200px;">
<!-- 栏目名称 -->
<div>
<!-- 圆角 -->
<div class="flex justify-center">
<div class="div-rounded"></div>
</div>
<!-- 英文 -->
<div class="flex justify-center mt-2">
<span class="span-en">{{item.sqldata[0].category.enname}}</span>
</div>
<!-- 中文 -->
<div class="flex justify-center">
<span class="span-ch">{{item.sqldata[0].category.name}}</span>
</div>
</div>
<!-- 栏目内容 -->
<div class="flex justify-between mt-5">
{# <img src="/public/api/template01/static/image/unit/unit01.jpg" >
<img src="/public/api/template01/static/image/unit/unit02.jpg" >
<img src="/public/api/template01/static/image/unit/unit03.jpg" >
<img src="/public/api/template01/static/image/unit/unit04.jpg" > #}
{% for item1 in item.sqldata %}
<img src="{{item1.attachment}}" >
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% endfor %}
# 五、网站其它页面:处理分页
- 控制器
app/controller/api/template01/api.js
//其它页面
async ortherpage(){
const { ctx,app } = this;
const data = await ctx.apiPage('News',{
category_id:8
});
console.log('看一下数据', JSON.parse(JSON.stringify(data)));
await ctx.renderApi({
tempType:'ortherpage',
},'api/template01/main_app.html');
}
- 路由
app/router/api/template01/router.js
router.get('/api/template/01/ortherpage', controller.api.template01.api.ortherpage);
- 扩展
app/extend/context.js
// 前端数据模型 + 分页
// 前端数据模型
async apiModelData(modelName, where = {}, options = {}) {
let page = this.query.page ? parseInt(this.query.page) : 1;
let limit = this.query.limit ? parseInt(this.query.limit) : 10;
let offset = (page - 1) * limit;
//如果没有传排序规则,则默认给它id降序
if (!options.order) {
options.order = [
['id', 'desc']
]
}
let data = await this.app.model[modelName].findAndCountAll({
limit: limit,
offset: offset,
where: where,
...options
});
//求得总分页数
let totalPage = Math.ceil(data.count / limit);
// -----------------------处理页面分页按钮网址上有其他参数的情况-----------------------------------
//网址有其他参数,如:http://127.0.0.1:7001/admin/manager/index?page=1&limit=3&keyword=哈哈&cid=100
// console.log(this.query);
let query = { ...this.query };
console.log(query);//{ page: '1', limit: '3', keyword: '哈哈', cid: '100' }
if (query.hasOwnProperty('page')) {
delete query.page;
}
if (query.hasOwnProperty('limit')) {
delete query.limit;
}
console.log(query);//{ keyword: '哈哈', cid: '100' }
// 对象转&拼接字符串
//{ keyword: '哈哈', cid: '100'} 转成 &keyword=哈哈&cid=100
const urlEncode = (param, key, encode) => {
if (param == null) return '';
var paramStr = '';
var t = typeof (param);
if (t == 'string' || t == 'number' || t == 'boolean') {
paramStr += '&' + key + '=' + ((encode == null || encode) ? encodeURIComponent(param) : param);
} else {
for (var i in param) {
var k = key == null ? i : key + (param instanceof Array ? '[' + i + ']' : '.' + i)
paramStr += urlEncode(param[i], k, encode)
}
}
return paramStr;
}
//调用urlEncode方法
query = urlEncode(query);
// console.log(query);//结果:&keyword=哈哈&cid=100 (转码后:&keyword=%E5%93%88%E5%93%88&cid=100)
// -----------------------处理页面分页按钮网址上有其他参数的情况-----------------------------------
return {
page,
limit,
query,
totalPage,
data
}
},
// 前端分页 -- 模版1
async apiPage(modelName, where = {}, options = {}) {
let modeldata = await this.apiModelData(modelName, where, options);
// console.log('看一下modeldata的数据',modeldata);
const page = modeldata.page;
const limit = modeldata.limit;
const query = modeldata.query;
const totalPage = modeldata.totalPage;
//分页模版代码
let pageEl = '';
for (let i = 1; i <= totalPage; i++) {
//判断当前页是否是active
let active = '';
if (i == page) {
active = 'active';
}
pageEl += `<li class="${active}"><a href="?page=${i}&limit=${limit}${query}">${i}</a></li>`;
}
//如果当前就是第一页,就不存在上一页,禁用上一页按钮,下一页同理
let prevDisabled = page <= 1 ? 'disabled' : '';
let nextDisabled = page >= totalPage ? 'disabled' : '';
let firstpage = page == 1 ? 'disabled' : '';
let lastpage = page == totalPage ? 'disabled' : '';
//最后将所有模版代码组装一起
let apiPageHtml = `
<style type="text/css">
.pages .disabled{
background-color: #f5f5f5;
}
.pages li.disabled a{
color: #aaaaaa;
}
.pages li.disabled{
position:relative;
}
.pages li.disabled::before{
content:'';
display:block;
/*background-color: red;*/
width:100%;
height:100%;
position:absolute;
left:0;
top:0;
}
</style>
<div class="flex justify-center py-5 mt-5"
style="flex:auto">
<ul class="pages">
<li class="${firstpage}"><a href="?page=1&limit=${limit}${query}">首页</a></li>
<li class="${prevDisabled}">
<a href="?page=${page - 1}&limit=${limit}${query}" aria-label="Previous">上一页</a>
</li>
${pageEl}
<li class="${nextDisabled}">
<a class="nextpage" href="?page=${page + 1}&limit=${limit}${query}">下一页</a>
</li>
<li class="${lastpage}"><a href="?page=${totalPage}&limit=${limit}${query}">尾页</a></li>
</ul>
</div>
`;
//如何将我们定义的这个apiPageHtml变量放在模版里面去?
//egg.js框架给我们在context扩展里面提供了locals对象,可将变量挂载到这个对象里面,然后可在模版里面使用
this.locals.apiPageHtml = apiPageHtml;
return modeldata.data.rows
},
- 模版
app/view/api/template01/main_app.html
...
{% if tempType == 'ortherpage' %}
{# 安全起见,egg.js官方默认将代码过滤成字符串,这里我们采用过滤器进行解码 #}
{{ ctx.locals.apiPageHtml | safe }}
{% endif %}
...