从0到1 koa-node-vue全栈开发(第四章: 使用vue, koa写一个前后端交互的列表搜索)

368 阅读2分钟

本章我们结合前面所学到的知识点, 可以来写一个搜索查询的小案例, 功能如需下:

电影列表搜索

搜索框

搜索内容

  • 具体的代码如下

前端代码

<template>
    <div class="MovieList">
        <div class="search">
            <el-input v-model="searchText" placeholder="请输入内容"></el-input>
            <el-button icon="el-icon-search" circle @click="searchList"></el-button>
        </div>
        <div class="content">
            <div class="item" v-for="item of list" :key="item.id">
                <div class="img">
                    <img :src="changeUrl(item.img)" alt="">
                </div>
                <div class="detail">
                    <div class="title">{{ item.nm }}</div>
                    <div class="resource-name">{{ item.enm }}</div>
                    <div class="movie-type">{{ item.cat }}</div>
                    <div class="time">{{ item.pubDesc }}</div>
                </div>
                <div class="score">
                    <p>{{ item.sc }}</p>
                </div>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'MovieList',
        data() {
            return {
                searchText: '',
                list: [],
            }
        },
        components: {},
        methods: {
            async searchList() {
                const { data: { list } } = await this.$http.get('/movie/subject_search', {
                    params: {
                        kw: this.searchText,
                    },
                })
                this.list = list
            },
            changeUrl(url) {
                return url.replace(/w.h/, '128.180')
            },
        },
    }
</script>
<style scoped lang="less">
    .MovieList {
        .search {
            display: flex;
            width: 350px;

            .el-input {
                margin-right: 10px;
            }
        }

        .content {
            width: 375px;

            .item {
                display: flex;
                justify-content: space-between;
                font-size: 12px;
                color: rgb(153, 153, 153);
                margin-bottom: 10px;

                .img {
                    width: 64px;

                    img {
                        width: 100%;
                    }
                }

                .detail {
                    flex: 1;
                    padding-left: 10px;
                    color: #222;

                    .title {
                        font-size: 16px;
                        font-weight: bold;
                    }

                }

                .score {
                    width: 48px;
                    color: #fa0;
                }
            }
        }
    }

</style>

后端代码

const Koa = require('koa')
const Router = require('koa-router')
const router = new Router()
const bodyparser = require('koa-bodyparser')
const axios = require('axios')
const app = new Koa()
app.use(bodyparser())

router.get('/movie/subject_search', async (ctx, next) => {
    const { request, response } = ctx
    const { query: { kw } } = request
    const { data: { movies } } = await axios.get('http://m.maoyan.com/ajax/search', {
        params: {
            kw,
            cityId: 10,
        },
    })
    response.body = movies
})


app.use(router.routes())
app.use(router.allowedMethods())
app.listen(3000, () => {
    console.log(`http://localhost:3000`)
})

登录验证

前面我们学习了jwt的登录验证, 在这一章, 我们将这个知识, 运用到里面

前端代码

<template>
    <div class="Login">
        <div class="content">
            <div class="username">
                <el-input placeholder="请输入用户名"
                          v-model="username">
                    <template slot="prepend">
                        <i class="el-icon-user"></i>
                    </template>
                </el-input>
            </div>
            <div class="password">
                <el-input placeholder="请输入密码"
                          show-password
                          v-model="password">
                    <template slot="prepend">
                        <i class="el-icon-lock"></i>
                    </template>
                </el-input>
            </div>
            <div class="button">
                <el-button type="primary" @click="login">L O G I N</el-button>
            </div>
            <div class="about">
                <el-tooltip class="item" effect="dark" content="微信扫码登录" placement="top">
                    <i class="el-icon-chat-line-round"></i>
                </el-tooltip>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'Login',
        components: {},
        data() {
            return {
                username: '',
                password: '',
            }
        },
        methods: {
            async login() {
                const { msgCode, message, data } = await this.$http.post('/login', {
                    username: this.username,
                    password: this.password,
                })
                if (msgCode === 200) {
                    // 将token放入本地存储里面
                    this.$storage.set('userInfo', data)
                    this.$message({
                        message,
                        type: 'success',
                    })
                    // 跳转到电影列表页面
                    this.$router.push('/movie-list')
                } else {
                    this.$message({
                        message,
                        type: 'error',
                    })
                }
            },
        },
    }
</script>

<style scoped lang="less">
    .Login {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        background: url("../assets/img/login_bg.jpg") no-repeat center center;

        .content {
            width: 300px;

            .username, .password {
                margin-bottom: 10px;

                i {
                    font-size: 18px;
                }
            }

            .button {
                margin-bottom: 10px;

                .el-button {
                    width: 100%;
                }
            }

            .about {
                display: flex;
                justify-content: flex-end;
                align-items: center;

                i {
                    cursor: pointer;
                }
            }
        }
    }
</style>

后端代码

router.post('/login', async (ctx, next) => {
    const { request, response } = ctx
    const { body } = request
    try {
        const { username, password } = body
        if (username && password) {
            // 生成token
            const token = sign({ username, password }, PRIVATE_KEY, { expiresIn: 60 * 60 })
            response.body = {
                msgCode: 200,
                message: '登录成功',
                data: {
                    token,
                    username,
                },
            }
        } else {
            response.body = {
                msgCode: 400,
                message: '请填写用户名和密码',
            }
        }
    } catch (e) {
        response.body = e
    }
})

// 生成token之后, 我们在需要验证的接口上面对token进行验证, 如果token验证失败, 那么我们就返回失败信息到前端

const verifyUser = async (ctx, next) => {
    const { request, response } = ctx
    const { header: { token } } = request
    const isVerify = verify(token, PRIVATE_KEY)
    try {
        const { username, password } = isVerify
        await next()
    } catch (e) {
        response.body = {
            msgCode: 404,
            message: 'token验证失败',
            errorMessage: e,
        }
    }
}

// 添加对电影列表查询接口的登录验证, 如果验证失败, 则直接返回404
router.get('/movie/subject_search', verifyUser, async (ctx, next) => {
    const { request, response } = ctx
    const { query: { kw } } = request
    const { data: { movies } } = await axios.get('http://m.maoyan.com/ajax/search', {
        params: {
            kw,
            cityId: 10,
        },
    })
    response.body = movies
})

到这一步, 我们就已经完成了一个简单的校验过程, 但是这里所有的文件, 都是在一个文件里面, 在下一章, 我们将对文件进行拆分