在之前的几篇文章中,我们做完了注册与登录,这次我们要真的开始做一个创作中心了,也就是写文章。
一个博客需要什么
我们现在前端中创建一个classes文件夹,放上我们需要的所有类。然后在其中创建一个article.js文件,来存放我们的文章类。一个文章类大致需要以下属性:
- 文章标题,一个字符串
- 文章作者,一个字符串
- 文章的唯一id,一个字符串,目前计划用户id+时间戳进行哈希
- 文章的内容,一个字符串
- 创建时间,使用JavaScript的Date类即可
- 点赞数,一个number类型
- .....
这是我们目前实现一个文章的最基础功能。后续还会继续添加。根据这个我们可以迅速写好一个class。构造函数中只需要传入标题、id和内容,其余的属性可以在构造函数中直接创建。
constructor(title, author, content){
this.title = title;
this.author = author;
this.createTime = Date.now();
// id使用MD5-16进行加密,这样可以生成一段16位的短id
this.id = MD5(`${author}${this.createTime}`).toString().slice(0,16);
this.content = content;
// 刚创建的文章怎么可能会有赞呢
this.likes = 0;
}
我们只实现了构造函数,接下来需要实现其他的功能,包括更新目前的文章信息到服务器、增加文章点赞数量,修改文章信息等。
updateTitle(newTitle){
if(this.title !==newTitle){
this.title = newTitle;
// updateToServer
}
}
updateContent(newContent){
// 因为content太长,在这里不做判断。
this.content = newContent;
// updateToServer
}
likesUp(){
this.likes++;
// updateToServer
}
likesDown(){
this.likes--;
// updateToServer
}
在写updateToServer函数之前,我们要先写好后端对应的接口。先通过内部的/createDatabase接口来创建对应的数据库。
app.get('/createDatabase',(req, res)=>{
connection.query(`create table articles
(
title varchar(50),
author varchar(20),
id varchar(16),
content text,
createTime varchar(20),
likes int(8)
)`,
(err,result)=>{
console.log(err);
res.send('ok');
});
})
数据库创建完毕后,就可以编写文章上传的接口了,用的也是post指令。文章id使用的是时间戳+用户名后md5,这样可以得到一个16位的id。
app.post('/addArticle',(req, res)=>{
const title = req.body.title;
const author = req.body.author;
const createTime = req.body.createTime;
const id = req.body.id;
const content = req.body.content;
const likes = req.body.likes;
const addSql = `insert into articles (title, author, id, content, createTime, likes) values(?,?,?,?,?,?)`;
const dataArray = [title,author,id, content, createTime, likes]
connection.query(addSql, dataArray, (err)=>{
if(err){
res.status(400).send(`${err}`);
}else{
res.send('新建文章成功!');
}
})
})
试着用apifox发送一个请求,如果成功的话就说明我们的代码没有问题。
前端页面的编写
我们在pages文件夹中创建一个articleEditor.vue组件,这就是我们的文章编辑器了。组件中大致包含这几要素:两个输入框,一个输入标题一个输入正文。一个博客预览界面,和一个提交按钮。先把页面主干写好并进行基本的路由添加。
<script setup>
import {ref} from 'vue'
import { Article } from '../classes/article';
import { useUserAccountStore } from '../stores/userAccount';
const userAccountStore = useUserAccountStore();
const articleInEditing = ref(new Article('', userAccountStore.username,''));
</script>
<template>
<input id="titleInput"/>
<textarea id="contentInput"></textarea>
<button>发布</button>
</template>
剩下的工作就好办了,给输入框绑定好响应式的属性,然后写好上传到服务器的函数即可。不过这里又出现了一个问题:我们解决了markdown的输入,又该如何解决markdown的渲染呢?自己手写一个当然好,但是这个不是我们博客的重点。因此我们使用bytemd包来进行markdown的输入和渲染,这是一个很现代化的前端包,对Vue和React等框架还有自己的优化。我们直接输入npm install @bytemd/vue-next,然后对前端代码进行一下改造即可。为了防止我们的渲染太难看,我们把所有的css全都删掉(之后会从头开始重新写css)。
<script setup>
import {ref} from 'vue'
import { Article } from '../classes/article';
import { useUserAccountStore } from '../stores/userAccount';
import { Editor} from '@bytemd/vue-next';
import gfm from '@bytemd/plugin-gfm'
import 'bytemd/dist/index.css'
const userAccountStore = useUserAccountStore();
const articleInEditing = ref(new Article('', userAccountStore.username,''));
// 插件,让其支持GFM语法
const plugins =[gfm(),];
function updateRendering(value){
articleInEditing.value.content = value;
}
</script>
<template>
<input id="titleInput" v-model="articleInEditing.title"/>
<!-- 由于没有支持v-model, 所以需要手动实现v-model-->
<Editor :value = "articleInEditing.content"
:plugins="plugins" @change="updateRendering" locate="zh"/>
<button>发布</button>
</template>
<style scoped>
</style>
不过在上传到服务器的时候,我们还需要考虑一个很严重的问题:我们传数据使用的是JSON,而JSON的格式决定了它需要很多的双引号,也就是说我们需要把单双引号等具有实际功能的字符妥善处理,也就是JSON转义。这里比较简单,直接把所有的双引号全部用转义字符替换即可。
function addArticle(){
articleInEditing.value.content = articleInEditing.value.content.replaceAll('"','\"');
axios.post('/server/addArticle',articleInEditing.value)
.then((response)=>{
alert(response.data);
})
.catch((err)=>{
alert(`发布文章失败,${err.response.data}`);
})
}
测试一下,发现可以上传了。