# 一、数据库数据准备

通过模版页面分析网站所需要的分类,并上传测试或者真实数据用于页面展示【具体看课程视频111课】

# 二、读取数据库数据渲染到模版页面(以合作单位为例)

  1. 控制器 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');
}
  1. 扩展 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);
  },
  1. 模版 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获取数据的方法到服务中,简化读取数据操作(以资讯中心为例)

  1. 控制器 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');
}
  1. 新建服务 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;

  1. 模版 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 %}
...

# 四、完善首页其它板块的数据展示

  1. 控制器 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');
}
  1. 模版 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 %}

# 五、网站其它页面:处理分页

  1. 控制器 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');
}
  1. 路由 app/router/api/template01/router.js
router.get('/api/template/01/ortherpage', controller.api.template01.api.ortherpage);
  1. 扩展 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
},
  1. 模版 app/view/api/template01/main_app.html
...
{% if tempType == 'ortherpage' %}
    {# 安全起见,egg.js官方默认将代码过滤成字符串,这里我们采用过滤器进行解码 #}
    {{ ctx.locals.apiPageHtml | safe }}
{% endif %}
...
更新时间: 2024年5月23日星期四下午4点07分