express项目总结

一、技术栈

想到什么记录什么,比较乱

express+mongodb+ejs

以及一堆第三方插件

效果总览:

请添加图片描述

目录介绍

请添加图片描述

config
  配置文件,主要配置了数据库访问路径

请添加图片描述

model
存放了 数据库每个集合(表)的 module 以及工具类
core.js 链接数据库

请添加图片描述

node_modules 第三方插件存放的地方
routers 路由配置
三个大模块 admin后台路由  index 前台路由  api 接口路由
其中admin文件夹下的都是admin后台的路由配置
admin下的main.js 是公共的

请添加图片描述

static  静态资源存放
-- admin  后台页面使用的css,js等资源
   -- bootstrap
   -- css
   --images
   -- js
-- upload 文件上传所保存的位置
-- wysiwyg-editor 富文本编辑器所使用的静态资源
views 界面文件(html)
和路由的配套
-- admin
  -- public 为公共的模块,抽离出来的
  比如:公共的头部,导航栏,以及成功,失败页面

请添加图片描述

app.js 项目入口 启动 node app.js
配置了所有共有使用的模块
package.json 依赖文件

一些

async 和await

由于 操作数据库是异步的,会出现代码执行了,而数据库还没操作完成的情况,会出现很多错误

因此 将方法定义为 async方法 操作数据库前 添加 await

二、第三方插件介绍

ejs

模板引擎,用来渲染后端的数据

const ejs = require("ejs"); 
// 配置模板引擎
app.engine('html',ejs.__express);
app.set('view engine', 'html');
//配置静态web目录
app.use(express.static("static"));

body-parser

用来接收表单提交的数据

const bodyParser = require("body-parser");
// 配置body-parser
app.use(bodyParser.urlencoded({ extended: false })); 
app.use(bodyParser.json());
// req.body 就可以获取表单穿的数据

express-session

配置session 用来拦截未登录的用户

const  session = require("express-session");
// 配置session
app.use(session({ 
    secret: 'this is session', //服务器端生成 session 的签名 
    name:"liu", //修改 session 对应 cookie 的名称 
    resave: false, //强制保存 session 即使它并没有变化 
    saveUninitialized: true, //强制将未初始化的 session 存储 
    cookie: { 
        maxAge:1000*60*30, 
        secure: false // true 表示只有 https 协议才能访问 cookie 
    },
    rolling:true //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false) 
    })
);

mongoose

连接数据库

const mongoose = require("mongoose");

multer

文件上传

const multer = require('multer');
let tools={
    multer(){

        var storage = multer.diskStorage({
            //配置上传的目录
            destination: async (req, file, cb)=>{
                //1、获取当前日期 20200703
                let day=sd.format(new Date(), 'YYYYMMDD');
                // static/upload/20200703
                let dir=path.join("static/upload",day)
                //2、按照日期生成图片存储目录  mkdirp是一个异步方法
                await mkdirp(dir)   
                
                cb(null, dir) //上传之前目录必须存在
            },
            //修改上传后的文件名
            filename: (req, file, cb)=> {
                //1、获取后缀名
                let extname= path.extname(file.originalname);
                //2、根据时间戳生成文件名
                cb(null, Date.now()+extname)
            }
        })
        
        var upload = multer({ storage: storage })

        return upload;
        
    }
}
// 使用 括号里的字段根据前端的属性来
tools.multer().single("focus_img")

silly-datetime

获得时间

const sd = require('silly-datetime');
var d=new Date()
return d.getTime()

svg-captcha

验证码

const svgCaptcha = require('svg-captcha');
router.get('/verify', function (req, res) { 
    var captcha = svgCaptcha.create(); 
    // 将验证码的文字信息存在session中
    req.session.captcha = captcha.text;
    res.type('svg'); 
    res.status(200).send(captcha.data); 
});

三、路由挂载

app.js

访问localhost:3000/ 跳转 index.js的路由

访问localhost:3000/admin 跳转 admin.js的路由

访问localhost:3000/api 跳转 api.js的路由

const admin = require("./routes/admin");
const index = require("./routes/index");
const api = require("./routes/api");

const app = express();
// 配置外部路由模块
app.use("/admin",admin);
app.use("/api",api);
app.use("/",index);

admin.js

访问localhost:3000/admin/ 跳转 main.js的路由

访问localhost:3000/admin/login 跳转 login.js的路由

const express = require("express");

var router = express.Router();
// 引入模块
const user = require("./admin/user");
const login = require("./admin/login");
const nav = require("./admin/nav");
const manager = require("./admin/manager");
const main = require("./admin/main");
const focus = require("./admin/focus");
const articleCate = require("./admin/articleCate");
const article = require("./admin/article");
const setting = require("./admin/setting");
// 挂载路由
router.use("/",main);
router.use("/user",user);
router.use("/login",login);
router.use("/nav",nav);
router.use("/manager",manager);
router.use("/focus",focus);
router.use("/articleCate",articleCate);
router.use("/article",article);
router.use("/setting",setting);

module.exports = router;

四、登录

1.配置前端页面

views/admin/login/login.html 提交路径为/admin/login/doLogin 方式 post

静态资源在 static/admin/

2.编写路由处理

routes/admin/login.js (先把这个路由挂载在admin.js中,admin.js又挂载在app.js)

// 1. 加载登录界面
// localhost:3000/admin/login
router.get("/", async(req,res)=>{
    res.render("admin/login/login.html");
})
// 2.验证码
router.get('/verify', function (req, res) { 
    var captcha = svgCaptcha.create(); 
    // 将验证码的文字信息存在session中
    req.session.captcha = captcha.text;
    res.type('svg'); 
    res.status(200).send(captcha.data); 
});

验证码的html

<dd>验 证 码:
    <input id="verify" type="text" name="verify">                           
      <img id="verify_img" src="/admin/login/verify"
           title="看不清?点击刷新" 
           onclick="javascript:this.src='/admin/login/verify?mt='+Math.random()">
                         </dd>	

3.处理登录请求

router.post("/doLogin",async (req,res)=>{
    // 获取登录表单的信息
    let verify = req.body.verify;
    let username = req.body.username;
    let password = req.body.password;
    // 输入的验证码小写与 正确的验证码小写 是否相等
    if(verify.toLocaleLowerCase()!= req.session.captcha.toLocaleLowerCase()){
        // 输入不正确
        res.render("admin/public/error.html",{
            "redirectUrl":"/admin/login",  // 回到登录页面
            "message": "验证码错误"

        });
    }
    // 判断用户名,密码是否正确
    // 根据表单提交的信息去数据库查询
    let result = await ManagerModel.find({"username":username,"password":md5(password)});
    // 如果存在该用户(用户名密码正确)
    if(result.length>0){
        // 保存用户信息在session中
        req.session.userinfo = result[0]; 
        // 输入正确
        res.render("admin/public/success.html",{
            "redirectUrl":"/admin",  // 回到登录页面
            "message": "正在前往登录"

         });
    }else {
        res.render("admin/public/error.html",{
            "redirectUrl":"/admin/login",  // 回到登录页面
            "message": "用户名或密码错误"
         });
    }
   

})

4.退出登录

// 退出登录
router.get("/loginOut",(req,res)=>{
    // 清空session
    req.session.userinfo = null;
    // 重定向到登录页面
    res.redirect("/admin/login");
})

5.配置拦截器

规定 /admin 下的所有路由都要经过登录后才能访问

在admin.js中配置 中间件 拦截器

原理: 路由请求前会先经过中间件 判断session是否存在用户,存在就放行,不存在就跳转到登录页面

//  配置拦截器
// 为了方便进行代码调试,先注释,需要再打开
// router.use((req,res,next)=>{
//     // 用url模块处理路由,防止get请求携带参数
//     let pathname = url.parse(req.url).pathname;
//     // 如果session中存在用户,放行
//     if(req.session.userinfo && req.session.userinfo.username){
//         next();
//     }else {
//         // 这些请求放行
//         if(pathname=="/login"||pathname=="/login/doLogin"||pathname=="/login/verify"){
//             next();
//         }else {
//             // 拦截剩余请求
//             // 重定向到登录页面
//             res.redirect("/admin/login");
//         }
//     }
// });

五、模块的增删查改

只记录一个,其他的都是一样的流程,只是绑定的属性,操作的集合不同

focus模块

1.准备前端页面

views/admin/focus/index.html

views/admin/focus/add.html

views/admin/focus/edit.html

2.配置路由

routes/admin/focus.js

router.get("/",async (req,res)=>{
    // 数据库查询所有数据
    let result = await FocusModel.find({});
	// 渲染到 admin/focus页面,携带数据list
    res.render("admin/focus",{
        list: result
    })
});

index.html 使用ejs模板引擎渲染

<%- include ("../public/page_header.html")%>
<div class="panel panel-default">
    <div class="table-responsive">
        <table class="table table-bordered">
            <thead>
                <tr class="th">
                    <th>名称</th>
                    <th>分类</th>
                    <th>图片</th>
                    <th>跳转地址</th>
                    <th class="text-center">排序</th>
                    <th class="text-center">状态</th>
                    <th class="text-center">操作</th>
                </tr>
            </thead>
            <tbody>
                <%for(var i=0;i<list.length;i++){%>
                <tr>
                    <td><%=list[i].title%></td>
                    <td>

                        <%if(list[i].type==1){%>
                        网站
                        <%}else if(list[i].type==2){%>
                        app
                        <%}else if(list[i].type==3){%>
                        小程序                   
                        <%}%>
                    
                    
                    </td>
                    <td>
                        <%if(list[i].focus_img){%>
                            <img src="/<%=list[i].focus_img%>" width="80" />                      
                        <%}%>                     

                    </td>


                    <td><%=list[i].link%></td>
                    <td class="text-center">
                        
                        <span class="chSpanNum" data-id="<%=list[i]._id%>" data-model="Focus" data-field="sort"><%=list[i].sort%></span>
                    
                    </span></td>
                    <td class="text-center">
                        <%if(list[i].status==1){%>
                            <!-- 自定义属性 model和字段以及id -->
                        <img src="/admin/images/yes.gif" class="chStatus" data-id="<%=list[i]._id%>" data-model="Focus" data-field="status" />

                        <%}else{%>
                        <img src="/admin/images/no.gif" class="chStatus" data-id="<%=list[i]._id%>" data-model="Focus" data-field="status" />
                        <%}%>

                     </td>

                     <td class="text-center">
                         <a href="/admin/focus/edit?id=<%=list[i]._id%>">修改</a>  
                        <a class="delete" href="/admin/focus/delete?id=<%=list[i]._id%>">删除</a></td>
                </tr>
                <%}%>
            </tbody>
        </table>
    </div>

</div>

// 先渲染到增加页面 
router.get("/add",(req,res)=>{
    res.render("admin/focus/add.html");
});
// 处理登录请求 
// 调用tools中封装好的上传文件方法
// focus_img 是前端页面的 name值
router.post("/doAdd",tools.multer().single('focus_img'), async(req,res)=>{
    // 存在图片则将路径前面的 static/ 去除后存储在数据库
    // 因为 访问static里的文件不需要带static/前缀,app.js配置了可以访问
    // 不存在则置为空
    let focus_img = req.file ? req.file.path.substr(7) : "";

    // 表单的其他数据都在req.body中, 拼接上focus_img 
    let result = new FocusModel(Object.assign(req.body,{"focus_img":focus_img}));
    await result.save();
    res.render("admin/public/success.html",{
        "redirectUrl":"/admin/focus",  // 回到列表页
        "message": "增加数据成功"
    });
});

注意点:

使用multer上传图片,需要在表单form 添加一个属性

enctype="multipart/form-data"

// 先获取到需要更改的那一个数据渲染到更改页面
router.get("/edit",async (req,res)=>{
    // req.quert.id 获取 get传值
    let id = req.query.id;
    // 去数据库查询数据
    let result = await FocusModel.find({"_id":id});
    // 也可以用findOne 这样传递的就是result
    // 渲染到 edit页面
    res.render("admin/focus/edit.html",{
        list: result[0] 
    });
});
// 处理更改请求
router.post("/doEdit",tools.multer().single("focus_img"),async (req,res)=>{
    // console.log(req.body);
    // console.log(req.file);
    // 先判断是否更新了 图片
    if(req.file){  // 更新了 图片
        // 去掉 static/
        let focus_img = req.file.path.substr(7);
        // Object.assign是用来拼接对象的
        await FocusModel.updateOne({"_id":req.body.id},
            Object.assign(req.body,{"focus_img":focus_img}));
        
    }else { // 没有更新图片直接更新body里的数据就可以了
        await FocusModel.updateOne({"_id":req.body.id},req.body);
    }
    res.render("admin/public/success.html",{
        "redirectUrl":"/admin/focus",  // 回到列表页
        "message": "修改数据成功"
    });
});

删除数据库数据的同时,删除上传到服务器的图片

router.get("/delete",async (req,res)=>{
    // 先获取数据
    let id = req.query.id;
    let result =await FocusModel.findOne({"_id":id});
    // console.log(result);
    // 获取路径名 存在就赋值,不存在置为空
    let focus_img = result.focus_img;
    // 根据id删除
    let delResult = await FocusModel.deleteOne({"_id":id});
    // console.log(delResult);
    // 判断是否删除成功
    if(delResult.deletedCount==1){
        // 存在图片
        if(focus_img){
            // 删除上传到服务器的图片
            // console.log("/static/"+focus_img);
            fs.unlink("static/"+focus_img, (err) => {
                console.log(err);
            })
        }
    }
    res.render("admin/public/success.html", {
        "redirectUrl": "/admin/focus",
        "message": "删除数据成功"
    })
})

六、界面

view/admin/main/index.html

配置iframe

<%- include ("../public/page_header.html")%>         
<nav class="navbar navbar-inverse" role="navigation">
    <div class="container-fluid">
        <div class="navbar-header">
            <img src="/admin/images/node.jpg" height="44px;" />
        </div>
        <div class="collapse navbar-collapse" id="example-navbar-collapse">           
            <ul class="nav navbar-nav navbar-right">
                <li><a>欢迎您,admin</a>
                </li>
                <li><a href="/admin/login/loginOut">安全退出</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<div class="container-fluid">
    <div class="row">
        <div class="col-sm-2">
        	<%- include ("../public/page_aside.html")%>          
        </div>
        <div class="col-sm-10">
          
            <iframe name="rightMain" style="border:none;" id="rightMain" src="/admin/welcome" frameborder="false" scrolling="auto" width="100%" height="100%">               
            </iframe>
        </div>
    </div>
</div>

</body>
</html>

page_aside.html

配置 target=“rightMain”

<ul class="aside">
         		
    <li>
        <h4>管理员管理</h4>
        
        <ul>
            <li  class="list-group-item"> <a href="/admin/manager" target="rightMain"> 管理员列表</a></li>
            
            <li class="list-group-item"> <a href="/admin/manager/add" target="rightMain">增加管理员</a></li>
        </ul>
    </li>
    <li>
        <h4>分类管理</h4>
        <ul>
            <li  class="list-group-item"> <a href="/admin/nav" target="rightMain"> 导航管理</a></li>
            
            <li class="list-group-item"> <a href="/admin/nav/add" target="rightMain">增加导航</a></li>
        </ul>
    </li>
    <li>
        <h4>轮播图管理</h4>
        <ul>
            <li  class="list-group-item"> <a href="/admin/focus" target="rightMain"> 轮播图管理</a></li>
            
            <li class="list-group-item"> <a href="/admin/focus/add" target="rightMain">增加轮播图</a></li>
        </ul>
    </li>
    <li>
        <h4>内容管理</h4>
        <ul>
            <li  class="list-group-item"> <a href="/admin/articleCate" target="rightMain"> 分类管理</a></li>
            
            <li class="list-group-item"> <a href="/admin/article" target="rightMain">内容管理</a></li>
        </ul>
    </li>
                     
</ul>		

这样通过侧边导航栏访问的页面就会出现在ifram框里面

上边导航栏

<ul id="myTab" class="nav nav-tabs">
                    <li class="active">
                        <a href="#index" data-toggle="tab">基本设置</a></li>
                    <li>
                        <a href="#info" data-toggle="tab">
                            内容详情
                        </a>
                    </li>
                    <li>
                        <a href="#seo" data-toggle="tab">
                            Seo设置
                        </a>
                    </li>
</ul>
<div id="myTabContent" class="tab-content">
                    <div class="tab-pane fade  in active" id="index">
                    </div>
                    <div class="tab-pane fade" id="info">
                        <textarea name="content" id="content" cols="30" rows="10"></textarea>
                    </div>
                    <div class="tab-pane fade" id="seo">             
 </div>

七、点击图标修改状态,点击数量修改数量

1.前端定义自定义属性和类

<span 
      class="chSpanNum" 
      data-id="<%=list[i]._id%>" 
      data-model="Focus" 
      data-field="sort">
    <%=list[i].sort%>
 </span>
    
<%if(list[i].status==1){%>
  <!-- 自定义属性 model和字段以及id -->
  <img src="/admin/images/yes.gif" 
          class="chStatus" 
          data-id="<%=list[i]._id%>" 
         data-model="Focus" 
         data-field="status" />
<%}else{%>
    <img src="/admin/images/no.gif"
          class="chStatus" 
           data-id="<%=list[i]._id%>" 
          data-model="Focus" data-field="status" />
 <%}%>

可以看到定义了两个类chStatus chSpanNum

static/admin/js/base.js中 编写触发事件和请求

var app = {
    init(){
        this.changeStatus();
		this.changeNum();
    },
    	// 鼠标点击 改变状态
	changeStatus: function (){
		// 绑定
		$(".chStatus").click(function(){
			// 获取 自定义属性的值
			var id = $(this).attr("data-id");
			var model = $(this).attr("data-model");
			var field = $(this).attr("data-field");
			var el = $(this); 
			// 异步请求
			$.get("/admin/changeStatus", // 请求路径
				{
					// 携带参数
					id:id,
					model:model,
					field:field
				},function(response){ // 回调函数
					//console.log(response)
					if(response.success){
						// 后端数据库更新成功
						// 如果原来是 yes的图片 改为no
						if(el.attr("src").indexOf("yes")!=-1){
							// 更改 src属性的值
							el.attr("src", "/admin/images/no.gif");
						}else{
							el.attr("src", "/admin/images/yes.gif");
						}
					}
				}
			)
		})
	},
	// 鼠标点击 更改数量
	changeNum: function(){
		// 1. 绑定
		$(".chSpanNum").click(function(){
			// 2. 获取自定义属性的值
			var id=$(this).attr("data-id");
			var model=$(this).attr("data-model");
			var field=$(this).attr("data-field");

			var spanEl=$(this);
			// 获取 span框里的数值
			//.html 和.html()是不一样的 切记切记
			var spanNum=$(this).html();
			// alert(spanNum)
			// 编写输入框,加到span框体里
			var input=$("<input value='' style='width:60px'/> ");			
			$(this).html(input);
			// console.log("执行了83行");
			// 此处jquery报错
			// 获得焦点,将原先的数值添加到输入框
			$(input).trigger('focus').val(spanNum);
			
			// console.log("执行了85行");
			// 停止冒泡
			$(input).click(function(e){
				e.stopPropagation();				
			})
			// 输入框失去焦点时触发事件
			$(input).blur(function(){
				// 获取输入框的新值
				var inputNum = $(this).val();
				// 简单的数据校验
				// 如果值是正数,添加到span标签里,负数或空置为零
				if(inputNum>0){
					spanEl.html(inputNum);
				}else {
					spanEl.html(0);
				}
				// 异步请求
				$.get("/admin/changeNum", // 请求路由
					// 携带的参数
					{id:id,model:model,field:field,num:inputNum},
					// 回调函数
					function(response){
						if(!response.success){
							console.log(response);
						}
					}
				)
			})
		})
	}
}

后台处理请求

router/admin/main.js

// 引入所有model
const FocusModel = require("../../model/focusModel");
const NavModel = require("../../model/navModel");
const ManagerModel = require("../../model/managerModel");
const ArticleCateModel = require("../../model/articleCateModel");
const ArticleModel = require("../../model/articleModel");
// 配置 所有的model
let appModel = {
    FocusModel: FocusModel,
    NavModel: NavModel,
    ManagerModel: ManagerModel,
    ArticleCateModel:ArticleCateModel,
    ArticleModel:ArticleModel
}
// 处理所有的 状态更改请求
/**
 * 前提知识 es6里面的属性名表达式
 * var aaa="name"
 * var obj = {
 *      [aaa]:"张三"
 * }
 * console.log(obj) 
 *  结果为 {"name":"张三"} 可以用字符串去匹配预先的对象
 */
router.get("/changeStatus",async (req,res)=>{
    // 获取get传值
    let id = req.query.id;
    // 传过来的是数据库的表名 需要拼接成model的形式
    let model = req.query.model + "Model";
    let field = req.query.field; // 要修改的字段 status
    let json; // 需要更新的数据
    // 去数据库查询此id 信息
    // model为拼接好的串,在appModel 里匹配到相应的Model
    let result = await appModel[model].find({"_id":id});
    // 结果存在
    if(result.length>0){
        // result是一个对象,获得第一个对象 用field去匹配相应的字段
        let tempField = result[0][field]; 
        //字段值是否为一 封装需要更新的数据对象
        tempField == 1?json={[field]:0} : json={[field]:1};
        // 数据库更改
        await appModel[model].updateOne({"_id":id},json);

        res.send({
            success: true,
            message: "修改状态成功"
        });
    }else {
        res.send({
            success: false,
            message: "修改状态失败"
        });
    }
});

router.get("/changeNum",async(req,res)=>{
    //  获得get传值
    let id = req.query.id;
    let model = req.query.model + "Model";
    let field = req.query.field; // 要修改的字段sort
    let num = req.query.num;
    // 先查询是否存在
    let result = await appModel[model].find({"_id":id});
    //结果存在
    if(result.length>0){
        let json = {
            // 例如 字段sort 的值是 num 
            [field]:num
        };
        await appModel[model].updateOne({"_id":id},json);
        res.send({
            success: true,
            message: "修改数量成功"
        });
    }else {
        res.send({
            success: false,
            message: "修改数量失败"
        });
    }

})

八、富文本编辑器

wysiwyg-editor

https://www.froala.com/wysiwyg-editor/docs/options

node中使用 建议参考官方文档

<!-- Include Editor style. --> 
<link href="https://cdn.jsdelivr.net/npm/froala-editor@latest/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" /> 
<!-- Create a tag that we will use as the editable area. --><!-- You can use a div tag as well. --> 
<textarea></textarea> 
<!-- Include Editor JS files. -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/froala-editor@latest/js/froala_editor.pkgd.min.js">
</script> 
<!-- Initialize the editor. --> 
<script> new FroalaEditor('textarea');</script>

汉化

https://www.froala.com/wysiwyg-editor/languages

1、引入 zh_cn 的语言包

2、配置 language: ‘zh_cn’

自定义导航条

https://www.froala.com/wysiwyg-editor/docs/options#toolbarBottom

new FroalaEditor('#content',{ 
    height: 300, 
    language: 'zh_cn', 
    toolbarButtons: [ ['bold', 'strikethrough', 'subscript', 'superscript', 'outdent', 'indent', 'clearFormatting', 'insertTable', 'html'] ,['undo', 'redo']], 
    toolbarButtonsXS: [ ['bold', 'strikethrough', 'subscript', 'superscript', 'outdent', 'indent', 'clearFormatting', 'insertTable', 'html'] , ['undo', 'redo']] });

上传图片

https://www.froala.com/wysiwyg-editor/docs/options#imageUploadURL

new FroalaEditor('#content', 
                 { height: 300, 
                  language: 'zh_cn', 
                  imageUploadURL: '/admin/goods/doUpload', })
router.post("/doUploadImage", multer().single('file'), async (req, res) => { 	var article_img = req.file ? req.file.path.substr(7) : ""; 
                                                                            res.send({link: "/"+article_img}); 
                                                                           
//注意:后台返回数据格式:{link: 'path/to/image.jpg'}                             })

九、分页

使用分页插件

1、首先引入jQuery和jqPaginator

2、定义一个空的div 让这个div的class pagination

https://v3.bootcss.com/components/#pagination

3、初始化

http://jqpaginator.keenwon.com/

$('#id').jqPaginator({
    totalPages: 100,
    visiblePages: 10,
    currentPage: 1,
    onPageChange: function (num, type) {
        $('#text').html('当前第' + num + '页');
    }
});

*db.表名.find().skip((page-1)pageSize).limit(pageSize)

<script>
    $('#pagination').jqPaginator({
        totalPages: <%=totalPages%>,
        visiblePages: 5,
        currentPage: <%= page %>,
        onPageChange: function (num, type) {
            if(type=="change"){
                location.href="/admin/article?page="+num+"&keywords=<%=keywords%>";
            }
            // console.log(type);    
        //    console.log('当前第' + num + '页')
        }
    })
</script>

十、配置全局变量

app.locals.adminPath=”experss_admin”
或者
req.app.locals.adminPath=”experss_admin”
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值