首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >2. 许愿墙(Node.js+Express+art-template+MySQL)

2. 许愿墙(Node.js+Express+art-template+MySQL)

作者头像
爱学习的程序媛
发布2022-04-07 16:04:45
发布2022-04-07 16:04:45
1.9K00
代码可运行
举报
文章被收录于专栏:学习/读书笔记学习/读书笔记
运行总次数:0
代码可运行

2.1 需求说明

1)展示最多50条用户的许愿信息;

2)许愿信息使用便签的形式粘贴在页面上;

3)便签背景色随机生成,并展示在页面的任意位置;

4)可以拖动便签;

5)可以添加许愿信息;

6)添加许愿信息的时候要进行验证,不允许提交空的姓名或空的许愿内容。

2.2 效果展示

本节项目对这本书的“许愿墙”demo做了优化,整个页面样式功能以及后端接口处理都做了优化,效果如下:

http://mpvideo.qpic.cn/0b78iqaaaaaakqamvix5jjpfargdabcaaaaa.f10002.mp4?dis_k=68ecb5078dcac6e528e122c80647d541&dis_t=1649318630&vid=wxv_1410936510232526848&format_id=10002&support_redirect=0&mmversion=false

2.3 创建MySQL数据库表

可以使用 mysql 可视化工具进行创建,数据库和数据库表都叫 wish。

wish表各字段及其作用:

可以添加一些模拟数据:

2.4 创建目录

2.4.1 生成项目目录 wish

2.4.2 安装依赖包

2.4.3 更改默认端口

默认端口为 3000,为了方便演示以及避免与其他项目冲突,将端口号改为 3001。

2.4.4 更换模板引擎

修改 app.js 文件:

代码语言:javascript
代码运行次数:0
运行
复制
// app.set('view engine', 'jade');
app.engine('html', require('express-art-template'));
app.set('view engine', 'html');

2.4.5 增加配置文件

代码语言:javascript
代码运行次数:0
运行
复制
//config.js

const config = {
    DEBUG: true,
    MYSQL: {
        host: 'localhost',
        database: 'wish',
        username: 'root',
        password: '123456'
    }
};

module.exports = config;

2.4.6 增加数据库配置文件

代码语言:javascript
代码运行次数:0
运行
复制
//db.js

const Sequelize = require('sequelize');
const config = require('./config');

const sequelize = new Sequelize(
    config.MYSQL.database,
    config.MYSQL.username,
    config.MYSQL.password, {
        host: config.MYSQL.host,
        dialect: 'mysql',
        logging: config.DEBUG ? console.log : false,//是否打印日志
        pool: {//配置数据库连接池
            max: 5,
            min: 0,
            idle: 10000
        },
        timezone: '+08:00'//时区设置
    }
);

module.exports = sequelize;

2.4.7 增加常量文件

代码语言:javascript
代码运行次数:0
运行
复制
//constant/constant.js

const obj = {
    DEFAULT_SUCCESS: {
        code: '00',
        msg: '操作成功'
    },
    DEFAULT_ERROR: {
        code: '01',
        msg: '操作失败'
    },
};

module.exports = obj;

2.4.8 增加数据库映射文件

代码语言:javascript
代码运行次数:0
运行
复制
//models/wish.js

const Sequelize = require('sequelize');
const db = require('../db');

const Wish = db.define('Wish', {
    id: {type: Sequelize.INTEGER, primaryKey: true, allowNull: false, autoIncrement: true},
    name: {type: Sequelize.STRING(20), allowNull: false},
    content: {type: Sequelize.STRING, allowNull: false}
}, {
    underscored: true,//是否支持驼峰
    tableName: 'wish'
});

module.exports = Wish;

2.4.9 增加路由处理方法文件

代码语言:javascript
代码运行次数:0
运行
复制
//controllers/index.js

const async = require('async');
const wishModel = require('../models/wish');
const constant = require('../constant/constant');

const exportObj = {
    getList,
    add
};

module.exports = exportObj;

// 获取许愿列表
function getList(req, res){
    //定义一个async任务
    let tasks = {
        //执行查询方法
        query: cb => {
            wishModel.findAll({
                limit: 50,
                order: [['created_at', 'DESC']]
            }).then(result => {
                let list = [];
                result.forEach(val => {
                    let date = new Date(val.createdAt);
                    date = date.getFullYear() + '.' + date.getMonth() + '.' + date.getDate();
                    let obj = {
                        id: val.id,
                        name: val.name,
                        content: val.content,
                        createdAt: date
                    };
                    list.push(obj);
                });
                cb(null, list);
            }).catch(err => {
                console.log(err);
                cb(constant.DEFAULT_ERROR);
            })
        }
    };
    async.auto(tasks, (err, result) => {
        if(err){
            console.log(err);
        }else {
            res.render('index', {
                list: result['query']
            })
        }
    })
}

// 添加许愿方法
function add(req, res){
    let tasks = {
        add: cb => {
            wishModel.create({
                name: req.body.name,
                content: req.body.content,
            }).then(result => {
                cb(constant.DEFAULT_SUCCESS);
            }).catch(err => {
                console.log(err);
                cb(constant.DEFAULT_ERROR);
            })
        }
    };
    async.auto(tasks, (err, result) => {
        if(err){
            console.log(err);
            res.send(err);
        }else {
            res.send(result);
        }
    })
}

2.4.10 增加路由文件

代码语言:javascript
代码运行次数:0
运行
复制
//routes/index.js

const express = require('express');
const router = express.Router();
const controller = require('../controllers/index');

router.get('/', controller.getList);
router.post('/add', controller.add);

module.exports = router;

2.5 前端页面

2.5.1 增加页面文件

代码语言:javascript
代码运行次数:0
运行
复制
//views/index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>许愿墙</title>
        <link rel="stylesheet" href="/stylesheets/style.css">
    </head>
    <body>
        <!--便签列表-->
        <div id="container" data-list="{{list}}" onmousemove="moveNodeObj.mouseMove()" onmouseup="moveNodeObj.mouseStop()"></div>
        <!--填写心愿弹窗-->
        <div class="mask-layer">
            <div class="container">
                <input id="name" class="input" name="name" placeholder="请输入您的姓名">
                <textarea id="content" class="input" name="content" rows="5" placeholder="请填写您的心愿"></textarea>
                <button class="form-btn close" onclick="closeAddNodeWin()">关闭</button>
                <button class="form-btn submit" onclick="submit()">提交</button>
                <!--提示信息-->
                <div id="msg" class="warn-msg"></div>
            </div>
        </div>
        <!--操作按钮-->
        <div class="opt-btn">
            <div class="add-btn" onclick="openAddNodeWin()">添加</div>
        </div>
        
        <script src="/javascripts/jquery-1.11.3.js"></script>
        <script src="/javascripts/index.js"></script>
    </body>
</html>

2.5.2 添加样式

代码语言:javascript
代码运行次数:0
运行
复制
//public/stylesheets/style.css

/*便签样式*/
.node {
    color: #fff;
    font-size: 12px;
    position: absolute;
    padding: 15px;
    max-width: 150px;
    border-radius: 5px;
    box-shadow: 0 0 2px 2px #ccc;
    cursor: move;
}
.node .content {
    margin-bottom: 10px;
}
.name-date {
    float: right;
    text-align: center;
}
/*提示框样式*/
.warn-msg {
    color: #fff;
    font-size: 14px;
    position: absolute;
    top: -30px; left: 50%;
    transform: translate(-50%);
    padding: 5px 10px;
    border-radius: 5px;
    min-width: 200px;
    text-align: center;
    display: none;
}
/*浮动菜单样式*/
.opt-btn {
    position: absolute;
    top: 0; left: 0;
    background: #f5f5f5;
    padding: 10px;
}
.opt-btn .add-btn {
    color: #fff;
    cursor: pointer;
    padding: 8px 16px;
    font-size: 12px;
    background: #3993e8;
    border-radius: 5px;
    display: inline-block;
}
/*遮罩层样式*/
.mask-layer {
    width: 100%;
    height: 100vh;
    position: absolute;
    left: 0; top: 0;
    background: rgba(0, 0, 0, 0.5);
    display: none;
}
.mask-layer .container {
    width: 500px;
    margin: 100px auto;
    background: #28cde4;
    text-align: center;
    padding: 40px 0;
    position: relative;
    border-radius: 5px;
}
.mask-layer .input {
    color: #333;
    display: block;
    width: 360px;
    margin: auto;
    outline: none;
    margin-bottom: 20px;
    padding: 5px;
    border: 1px solid transparent;
    border-radius: 5px;
}
.mask-layer .form-btn {
    color: #fff;
    outline: none;
    cursor: pointer;
    padding: 5px 16px;
    background: #eb73dd;
    border-radius: 5px;
    border: 1px solid transparent;
}
.mask-layer .close {
    background: #6ba2eb;
}

2.5.3 处理逻辑

代码语言:javascript
代码运行次数:0
运行
复制
//public/javascripts/index.js

//生成随机颜色
function createRandomColor(){
    let r = Math.floor(Math.random() * (200 - 100 + 1)) + 100;
    let g = Math.floor(Math.random() * (200 - 100 + 1)) + 100;
    let b = Math.floor(Math.random() * (200 - 100 + 1)) + 100;
    return `rgb(${r}, ${g}, ${b})`;
}

//生成随机位置
function createRandomPosition(){
    let width = window.screen.width - 300;
    let height = window.screen.height - 300;
    let left = Math.floor(Math.random() * width - 200 + 1) + 200;
    let top = Math.floor(Math.random() * height - 200 + 1) + 200;
    return {left, top}
}

//移动便签
let moveNodeObj = {
    contentId: '',//当前移动的便签id
    isMouse: false,//是否移动
    startX: 0,//移动起始x
    startY: 0,//移动起始y
    endX: 0,//移动终点x
    endY: 0,//移动终点y
    mouseStart(ev){
        let e = ev || window.event;
        this.startX = e.clientX;
        this.startY = e.clientY;
        this.isMouse = true;
        this.contentId = '#' + $(e.target).attr('id');
    },
    mouseMove(ev){
        if(this.isMouse){
            let e = ev || window.event;
            this.endX = e.clientX;
            this.endY = e.clientY;
            let mouseX = this.endX - this.startX;
            let mouseY = this.endY - this.startY;
            let content = $(this.contentId);
            let offset = content.offset();
            if(offset){
                let left = offset.left + mouseX;
                let top = offset.top + mouseY;
                content.css({left: left, top: top});
                this.startX = this.endX;
                this.startY = this.endY;
            }

        }
    },
    mouseStop(){
        this.isMouse = false;
    }
};

// 初始化数据
$(() => {
    let container = $('#container');
    let list = JSON.parse(container.attr('data-list'));
    list.forEach(val => {
        createNode(val.id, val.name, val.content, val.createdAt, container);
    })
});

//创建许愿便签
function createNode(id, name, content, createdAt, container){
    let bgColor = createRandomColor();
    let p = createRandomPosition();
    $(`<div class="node" id="id${id}" onmousedown="moveNodeObj.mouseStart()">
            <div class="content">${content}</div>
            <div class="name-date">
                <div>${name}</div>
                <div>${createdAt}</div>
            </div>
        </div>`).css({
        background: bgColor,
        top: p.top,
        left: p.left,
    }).appendTo(container);
}

// 显示提示信息
function showWarnMsg(text, color){
    let msg = $('#msg');
    msg.css({background: color}).html(text);
    msg.show();
    setTimeout(() => {
        msg.hide();
    }, 1500)
}

// 打开添加心愿窗口
function openAddNodeWin(){
    $('.mask-layer').show();
}

//关闭添加心愿窗口
function closeAddNodeWin(){
    $('.mask-layer').hide();
}

//提交许愿
function submit(){
    let name = $('#name').val();
    let content = $('#content').val();
    if(name && content){
        $.ajax({
            url: '/add',
            type: 'POST',
            data: {
                name: name,
                content: content
            },
            success: data => {
                showWarnMsg(data.msg, '#2fcc4f');
                setTimeout(() => {
                    closeAddNodeWin();
                }, 1500);
            },
            error: (err) => {
                showWarnMsg(err.msg, '#eaba1b');
            }
        });
    }else {
        showWarnMsg('请填写您的姓名和心愿!', '#eaba1b');
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱学习的程序媛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档