Django-Vue搭建个人博客:文章更新与删除
5202 views, 2023/10/17 updated Go to Comments
本章来完成文章更新和删除功能。
来看看具体怎么做吧。
更新与删除
文章更新实际上和文章发表非常类似,有了前面的基础就比较简单了,因此这里把代码一口气全贴出来。
新建 frontend/src/views/ArticleEdit.vue
文件,写入:
<!-- frontend/src/views/ArticleEdit.vue -->
<template>
<BlogHeader/>
<div id="article-create">
<h3>更新文章</h3>
<form>
<div class="form-elem">
<span>标题:</span>
<input v-model="title" type="text" placeholder="输入标题">
</div>
<div class="form-elem">
<span>分类:</span>
<span
v-for="category in categories"
:key="category.id"
>
<!--样式也可以通过 :style 绑定-->
<button
class="category-btn"
:style="categoryStyle(category)"
@click.prevent="chooseCategory(category)"
>
{{category.title}}
</button>
</span>
</div>
<div class="form-elem">
<span>标签:</span>
<input v-model="tags" type="text" placeholder="输入标签,用逗号分隔">
</div>
<div class="form-elem">
<span>正文:</span>
<textarea v-model="body" placeholder="输入正文" rows="20" cols="80"></textarea>
</div>
<div class="form-elem">
<button v-on:click.prevent="submit">提交</button>
</div>
<div class="form-elem">
<button v-on:click.prevent="deleteArticle" style="background-color: darkred">删除</button>
</div>
</form>
</div>
<BlogFooter/>
</template>
<script>
import BlogHeader from '@/components/BlogHeader.vue'
import BlogFooter from '@/components/BlogFooter.vue'
import axios from 'axios';
import authorization from '@/utils/authorization';
export default {
name: 'ArticleEdit',
components: {BlogHeader, BlogFooter},
data: function () {
return {
title: '',
body: '',
// 所有分类
categories: [],
// 选定的分类
selectedCategory: null,
// 标签
tags: '',
// Article id
articleID: null,
}
},
mounted() {
// 页面初始化时获取所有分类
axios
.get('/api/category/')
.then(response => this.categories = response.data);
// 与前面章节说的一样
// 如果你不希望非管理员用户也能获取原始 Markdown 数据
// 那么必须在后端进行鉴权
// 根据用户身份选用不同的序列化器
const that = this;
axios
.get('/api/article/' + that.$route.params.id + '/')
.then(function (response) {
const data = response.data;
that.title = data.title;
that.body = data.body;
that.selectedCategory = data.category;
that.tags = data.tags.join(',');
that.articleID = data.id;
})
},
methods: {
// 根据分类是否被选中,按钮的颜色发生变化
categoryStyle(category) {
if (this.selectedCategory !== null && category.id === this.selectedCategory.id) {
return {
backgroundColor: 'black',
}
}
return {
backgroundColor: 'lightgrey',
color: 'black',
}
},
// 选取分类
chooseCategory(category) {
// 如果点击已选取的分类,则将 selectedCategory 置空
if (this.selectedCategory !== null && this.selectedCategory.id === category.id) {
this.selectedCategory = null
}
else {
this.selectedCategory = category;
}
},
// 点击提交按钮
// 大部分代码与发表文章相同
// 有少量改动
submit() {
const that = this;
authorization()
.then(function (response) {
if (response[0]) {
let data = {
title: that.title,
body: that.body,
};
data.category_id = that.selectedCategory ? that.selectedCategory.id : null;
data.tags = that.tags
.split(/[,,]/)
.map(x => x.trim())
.filter(x => x.charAt(0) !== '');
const token = localStorage.getItem('access.myblog');
axios
.put('/api/article/' + that.articleID + '/',
data,
{
headers: {Authorization: 'Bearer ' + token}
})
.then(function (response) {
that.$router.push({name: 'ArticleDetail', params: {id: response.data.id}});
})
}
else {
alert('令牌过期,请重新登录。')
}
}
)
},
deleteArticle() {
const that = this;
const token = localStorage.getItem('access.myblog');
authorization()
.then(function (response) {
if (response[0]) {
axios
.delete('/api/article/' + that.articleID + '/',
{
headers: {Authorization: 'Bearer ' + token}
})
.then(() => that.$router.push({name: 'Home'}))
}
else {
alert('令牌过期,请重新登录。')
}
}
)
}
}
}
</script>
<style scoped>
.category-btn {
margin-right: 10px;
}
#article-create {
text-align: center;
font-size: large;
}
form {
text-align: left;
padding-left: 100px;
padding-right: 10px;
}
.form-elem {
padding: 10px;
}
input {
height: 25px;
padding-left: 10px;
width: 50%;
}
button {
height: 35px;
cursor: pointer;
border: none;
outline: none;
background: steelblue;
color: whitesmoke;
border-radius: 5px;
width: 60px;
}
</style>
代码仔细一看,和发表文章几乎大部分内容都是一样的,但还是有以下区别:
- Vue 管理的数据中多了
articleID
,用于获取需要更新的文章的索引,以便更新或删除时使用。 - 页面在初始化
mounted
时,将已有的文章旧数据(比如标题、标签、正文等)填入表单控件。 - 提交按钮将数据
PUT
到原文章进行更新(注意此处分类的赋值代码有些许改动)。 - 新增了删除的方法。
这里建议读者逐行对比和发表文章的代码,想想为什么代码之间会有些小区别。
代码相似就说明有优化和合并的可能。教程就不作赘述了,留给读者以后自行优化。
提示:利用 Props 确定是发表还是更新。
另一个问题是,文章更新页面未作鉴权。虽然无权限用户不能做更改资源的操作,但还是可以拿到原始 Markdown 正文的。如果你很在意这个,那就对相关接口做一次鉴权,根据权限来返回不同的数据(或者给原始正文单独一个带权限的接口)。
入口及其他
剩下的工作就比较简单了。
首先老规矩,注册路由:
// frontend/src/router/index.js
...
import ArticleEdit from "@/views/ArticleEdit.vue";
const routes = [
...
{
path: "/article/edit/:id",
name: "ArticleEdit",
component: ArticleEdit
},
];
...
接着在文章详情页中放一个更新和删除页面的入口:
<!-- frontend/src/views/ArticleDetail.vue -->
<template>
...
<p id="subtitle">
...
<span v-if="isSuperuser">
<router-link
:to="{ name: 'ArticleEdit', params: { id: article.id }}"
>
更新与删除
</router-link>
</span>
</p>
...
</template>
<script>
...
computed: {
isSuperuser() {
return localStorage.getItem('isSuperuser.myblog') === 'true'
}
}
...
</script>
如果是管理员用户,则显示此入口。(这里也用到了计算属性)
完成了,让我们测试一下。
详情页面的入口:
点击之后:
进入页面后会自动填充入旧数据,并且更新和删除都是能正常工作了。
课后作业
文章的增删改查就做完了,感觉如何?如果你可以不看资料独立完成,那么恭喜你,知识掌握得比较扎实了。
分类的增删改查不做也可以,毕竟博客而已,大不了后台改就 ok 了。教程就不讲重复的内容了,读者自行完成吧。