更新删除博客前端
1.API
src\api\post.js
// 更新博客
export function update(topic) {
return request({
url: '/post/update',
method: 'post',
data: topic
})
}
// 根据id删除博客
export function deleteTopic(id) {
return request({
url: `/post/delete/${id}`,
method: 'delete'
})
}
路由
src\router\index.js
// 文章修改编辑
{
name: 'topic-edit',
path: '/topic/edit/:id',
component: () => import('@/views/post/Edit'),
meta: {
title: '编辑',
requireAuth: true
}
}
新增Edit页面
src\views\post\新增Edit.vue
<template>
<section>
<div class="columns">
<div class="column is-full">
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span><i class="fa fa fa-book"> 主题 / 更新主题</i></span>
</div>
<div>
<el-form :model="topic" ref="topic" class="demo-topic">
<el-form-item prop="title">
<el-input
v-model="topic.title"
placeholder="输入新的主题名称"
></el-input>
</el-form-item>
<!--Markdown-->
<div id="vditor"></div>
<b-taginput
v-model="tags"
class="my-3"
maxlength="15"
maxtags="3"
ellipsis
placeholder="请输入主题标签,限制为 15 个字符和 3 个标签"
/>
<el-form-item class="mt-3">
<el-button type="primary" @click="handleUpdate()"
>更新
</el-button>
<el-button @click="resetForm('topic')">重置</el-button>
</el-form-item>
</el-form>
</div>
</el-card>
</div>
</div>
</section>
</template>
<script>
import { getTopic, update } from "@/api/post";
import Vditor from "vditor";
import "vditor/dist/index.css";
export default {
name: "TopicEdit",
components: {},
data() {
return {
topic: {},
tags: [],
};
},
created() {
this.fetchTopic();
},
methods: {
renderMarkdown(md) {
this.contentEditor = new Vditor("vditor", {
height: 460,
placeholder: "输入要更新的内容",
preview: {
hljs: { style: "monokai" },
},
mode: "sv",
after: () => {
this.contentEditor.setValue(md);
},
});
},
fetchTopic() {
getTopic(this.$route.params.id).then((value) => {
this.topic = value.data.topic;
this.tags = value.data.tags.map(tag => tag.name);
this.renderMarkdown(this.topic.content);
});
},
handleUpdate: function () {
this.topic.content = this.contentEditor.getValue();
update(this.topic).then((response) => {
const { data } = response;
console.log(data);
setTimeout(() => {
this.$router.push({
name: "post-detail",
params: { id: data.id },
});
}, 800);
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.contentEditor.setValue("");
this.tags = "";
},
},
};
</script>
<style>
.vditor-reset pre > code {
font-size: 100%;
}
</style>
更新删除博客后端
BmsPostController
/**
* 修改文章
* @param userName
* @param post
* @return
*/
@PostMapping("/update")
public ApiResult<BmsPost> update(@RequestHeader(value = "userName") String userName, @Valid @RequestBody BmsPost post) {
post = postService.updateById(userName, post);
return ApiResult.success(post);
}
@DeleteMapping("/delete/{id}")
public ApiResult<String> delete(@RequestHeader(value = "userName") String userName, @PathVariable("id") String id) {
postService.deleteById(userName,id);
return ApiResult.success(null, "删除成功");
}
BmsPostService
public BmsPost updateById(String userName, BmsPost post) {
// 获取用户信息
UmsUser umsUser = userService.getOne(new LambdaQueryWrapper<UmsUser>().eq(UmsUser::getUsername, userName));
Assert.notNull(umsUser, "用户不存在");
Assert.isTrue(umsUser.getId().equals(post.getUserId()), "非本人无权修改");
post.setModifyTime(new Date());
post.setContent(EmojiParser.parseToAliases(post.getContent()));
this.updateById(post);
return post;
}
public void deleteById(String userName, String id) {
UmsUser umsUser = userService.getOne(new LambdaQueryWrapper<UmsUser>().eq(UmsUser::getUsername, userName));
Assert.notNull(umsUser, "用户不存在");
BmsPost post = this.getById(id);
Assert.notNull(post, "来晚一步,话题已不存在");
Assert.isTrue(post.getUserId().equals(umsUser.getId()), "你为什么可以删除别人的话题???");
this.removeById(id);
}
tag标签前端
API
src/api/新增tag.js
import request from '@/utils/request'
export function getTopicsByTag(paramMap) {
return request({
url: '/tag/' + paramMap.name,
method: 'get',
params: {
page: paramMap.page,
size: paramMap.size
}
})
}
路由
src\router\index.js
,
{
name: 'tag',
path: '/tag/:name',
component: () => import('@/views/tag/Tag'),
meta: { title: '主题列表' }
}
新增Tag.vue
src/views/新增tag/Tag.vue
<template>
<div id="tag" class="columns">
<div class="column is-three-quarters">
<el-card class="box-card" shadow="never">
<div slot="header" class="">
🔍 检索到 <span class="has-text-info">{{ topics.length }}</span> 篇有关
<span class="has-text-info">{{ this.$route.params.name }}</span>
的话题
</div>
<div class="text item">
<article v-for="(item, index) in topics" :key="index" class="media mt-3">
<div class="media-content">
<div class="content">
<el-tooltip class="item" effect="dark" :content="item.title" placement="top">
<router-link :to="{ name: 'post-detail',params:{id: item.id } }">
{{ item.title }}
</router-link>
</el-tooltip>
</div>
<nav class="level has-text-grey is-size-7">
<div class="level-left">
<span>发布于:{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
<span class="mx-3">浏览:{{ item.view }}</span>
<span>评论:{{ item.comments }}</span>
</div>
</nav>
</div>
</article>
</div>
</el-card>
</div>
<div class="column">
<el-card class="box-card" shadow="hover">
<div slot="header" class="clearfix">
🤙 关于标签
</div>
<div>
<ul>
<li class="content">标签由平台用户发布使用</li>
<li class="content">系统每周会定时清理无用标签</li>
</ul>
</div>
</el-card>
<el-card shadow="hover">
<div slot="header">
🏷 热门标签
</div>
<div>
<ul>
<li v-for="(tag,index) in tags" :key="index" style="padding: 6px 0">
<router-link :to="{name:'tag',params:{name:tag.name}}">
<span v-if="index<9" class="tag">
0{{ parseInt(index) + 1 }}
</span>
<span v-else class="tag">
{{ parseInt(index) + 1 }}
</span>
{{ tag.name }}
</router-link>
</li>
</ul>
</div>
</el-card>
</div>
</div>
</template>
<script>
import { getTopicsByTag } from '@/api/tag'
export default {
name: 'Tag',
data() {
return {
topics: [],
tags: [],
paramMap: {
name: this.$route.params.name,
page: 1,
size: 10
}
}
},
created() {
this.fetchList()
},
methods: {
fetchList: function() {
getTopicsByTag(this.paramMap).then(response => {
console.log(response)
this.topics = response.data.topics.records
this.tags = response.data.hotTags.records
})
}
}
}
</script>
<style scoped>
#tag {
min-height: 500px;
}
</style>
tag标签后端
BmsTagController
package com.notepad.blog.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.notepad.blog.common.api.ApiResult;
import com.notepad.blog.domain.BmsPost;
import com.notepad.blog.domain.BmsTag;
import com.notepad.blog.service.BmsTagService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/tag")
public class BmsTagController {
@Resource
private BmsTagService tagService;
@GetMapping("/{name}")
public ApiResult<Map<String, Object>> getTopicsByTag(
@PathVariable("name") String tagName,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size) {
Map<String, Object> map = new HashMap<>(16);
LambdaQueryWrapper<BmsTag> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(BmsTag::getName, tagName);
BmsTag one = tagService.getOne(wrapper);
Assert.notNull(one, "话题不存在,或已被管理员删除");
Page<BmsPost> topics = tagService.selectTopicsByTagId(new Page<>(page, size), one.getId());
// 其他热门标签
Page<BmsTag> hotTags = tagService.page(new Page<>(1, 10),
new LambdaQueryWrapper<BmsTag>()
.notIn(BmsTag::getName, tagName)
.orderByDesc(BmsTag::getPostCount));
map.put("topics", topics);
map.put("hotTags", hotTags);
return ApiResult.success(map);
}
}
BmsTagService
public Page<BmsPost> selectTopicsByTagId(Page<BmsPost> topicPage, String id) {
// 获取关联的话题ID
Set<String> ids =postTagService.selectPostIdsByTagId(id);
LambdaQueryWrapper<BmsPost> wrapper = new LambdaQueryWrapper<>();
wrapper.in(BmsPost::getId, ids);
return postService.page(topicPage, wrapper);
}
BmsTagMapper.xml
<select id="getTopicIdsByTagId" resultType="java.lang.String">
SELECT t.post_id
from bms_post_tag t
where t.tag_id = #{id}
</select>
搜索前端实现
API
src\api\新增search.js
import request from '@/utils/request'
// 关键词检索
export function searchByKeyword(query) {
return request({
url: `/search`,
method: 'get',
params: {
keyword: query.keyword,
pageNum: query.pageNum,
pageSize: query.pageSize
}
})
}
路由
src\router\index.js
,
// 搜索
{
name: 'search',
path: '/search',
component: () => import('@/views/Search'),
meta: { title: '检索' }
}
新增Search.vue
src/views/Search
<template>
<div>
<el-card shadow="never">
<div slot="header" class="clearfix">
检索到 <code>{{ list.length }}</code>
条关于 <code class="has-text-info">{{ query.keyword }}</code> 的记录
</div>
<div>
<article v-for="(item, index) in list" :key="index" class="media">
<div class="media-left">
<figure class="image is-48x48">
<img :src="`https://cn.gravatar.com/avatar/${item.userId}?s=164&d=monsterid`">
</figure>
</div>
<div class="media-content">
<div class="">
<p class="ellipsis is-ellipsis-1">
<el-tooltip class="item" effect="dark" :content="item.title" placement="top">
<router-link :to="{name:'post-detail',params:{id:item.id}}">
<span class="is-size-6">{{ item.title }}</span>
</router-link>
</el-tooltip>
</p>
</div>
<nav class="level has-text-grey is-mobile is-size-7 mt-2">
<div class="level-left">
<div class="level-left">
<router-link class="level-item" :to="{ path: `/member/${item.username}/home` }">
{{ item.alias }}
</router-link>
<span class="mr-1">
发布于:{{ dayjs(item.createTime).format("YYYY/MM/DD") }}
</span>
<span
v-for="(tag, index) in item.tags"
:key="index"
class="tag is-hidden-mobile is-success is-light mr-1"
>
<router-link :to="{ name: 'tag', params: { name: tag.name } }">
{{ "#" + tag.name }}
</router-link>
</span>
<span class="is-hidden-mobile">浏览:{{ item.view }}</span>
</div>
</div>
</nav>
</div>
<div class="media-right" />
</article>
</div>
<!--分页-->
<pagination
v-show="query.total > 0"
:total="query.total"
:page.sync="query.pageNum"
:limit.sync="query.pageSize"
@pagination="fetchList"
/>
</el-card>
</div>
</template>
<script>
import { searchByKeyword } from '@/api/search'
import Pagination from '@/components/Pagination'
export default {
name: 'Search',
components: { Pagination },
data() {
return {
list: [],
query: {
keyword: this.$route.query.key,
pageNum: 1,
pageSize: 10,
total: 0
}
}
},
created() {
this.fetchList()
},
methods: {
fetchList() {
searchByKeyword(this.query).then(value => {
const { data } = value
this.list = data.records
this.query.total = data.total
this.query.pageSize = data.size
this.query.pageNum = data.current
})
}
}
}
</script>
<style scoped>
</style>
搜索后端实现
BmsSearchController
@RestController
@RequestMapping("/search")
public class BmsSearchController {
@Resource
private BmsPostService postService;
@GetMapping
public ApiResult<Page<PostVO>> searchList(@RequestParam("keyword") String keyword,
@RequestParam("pageNum") Integer pageNum,
@RequestParam("pageSize") Integer pageSize) {
Page<PostVO> results = postService.searchByKey(keyword, new Page<>(pageNum, pageSize));
return ApiResult.success(results);
}
}
BmsPostService
public Page<PostVO> searchByKey(String keyword, Page<PostVO> page) {
// 查询话题
Page<PostVO> iPage = this.baseMapper.searchByKey(page, keyword);
return iPage;
}
BmsPostMapper.xml
<select id="searchByKey" resultType="com.notepad.blog.domain.vo.PostVO">
SELECT t.id,
t.title,
t.user_id,
t.comments,
t.view,
t.collects,
t.top,
t.essence,
t.create_time,
t.modify_time,
u.username,
u.alias,
u.avatar
FROM bms_post t
LEFT JOIN ums_user u
ON t.user_id = u.id
<where>
<if test="keyword!=null">
and t.title like CONCAT('%','${keyword}')
or t.title like CONCAT('${keyword}','%')
or t.title like CONCAT('%','${keyword}','%')
or t.title = #{keyword}
</if>
</where>
order by t.view desc, t.create_time desc
</select>
用户中心前端
API
src\api新增\user.js
import request from '@/utils/request'
// 用户主页
export function getInfoByName(username, page, size) {
return request({
url: '/auth/user/' + username,
method: 'get',
params: {
pageNo: page,
size: size
}
})
}
路由
src\router\index.js
// 用户主页
{
name: 'user',
path: '/member/:username/home',
component: () => import('@/views/user/Profile'),
meta: { title: '用户中心' }
}
新增user\Profile.vue
src\views\新增user\Profile.vue
<template>
<div class="member">
<div class="columns">
<div class="column is-one-quarter">
<el-card shadow="never">
<div slot="header" class="has-text-centered">
<el-avatar :size="64" :src="`https://cn.gravatar.com/avatar/${topicUser.id}?s=164&d=monsterid`" />
<p class="mt-3">{{ topicUser.alias || topicUser.username }}</p>
</div>
<div>
<p class="content">积分:<code>{{ topicUser.score }}</code></p>
<p class="content">入驻:{{ dayjs(topicUser.createTime).format("YYYY/MM/DD HH:MM:ss") }}</p>
<p class="content">简介:{{ topicUser.bio }}</p>
</div>
</el-card>
</div>
<div class="column">
<!--用户发布的话题-->
<el-card class="box-card content" shadow="never">
<div slot="header" class="has-text-weight-bold">
<span>话题</span>
</div>
<div v-if="topics.length===0">
暂无话题
</div>
<div v-else class="topicUser-info">
<article v-for="(item, index) in topics" :key="index" class="media">
<div class="media-content">
<div class="content ellipsis is-ellipsis-1">
<el-tooltip class="item" effect="dark" :content="item.title" placement="top">
<router-link :to="{ name: 'post-detail', params: { id: item.id } }">
{{ item.title }}
</router-link>
</el-tooltip>
</div>
<nav class="level has-text-grey is-size-7">
<div class="level-left">
<span class="mr-1">
发布于:{{ dayjs(item.createTime).format("YYYY/MM/DD HH:mm:ss") }}
</span>
</div>
</nav>
</div>
<div v-if="token" class="media-right">
<div v-if="topicUser.username === user.username" class="level">
<div class="level-item mr-1">
<router-link :to="{name:'topic-edit',params: {id:item.id}}">
<span class="tag is-warning">编辑</span>
</router-link>
</div>
<div class="level-item">
<a @click="handleDelete(item.id)">
<span class="tag is-danger">删除</span>
</a>
</div>
</div>
</div>
</article>
</div>
<!--分页-->
<pagination
v-show="page.total > 0"
class="mt-5"
:total="page.total"
:page.sync="page.current"
:limit.sync="page.size"
@pagination="fetchUserById"
/>
</el-card>
</div>
</div>
</div>
</template>
<script>
import { getInfoByName } from '@/api/user'
import pagination from '@/components/Pagination/index'
import { mapGetters } from 'vuex'
import { deleteTopic } from '@/api/post'
export default {
name: 'Profile',
components: { pagination },
data() {
return {
topicUser: {},
topics: {},
page: {
current: 1,
size: 5,
total: 0
}
}
},
computed: {
...mapGetters(['token', 'user'])
},
created() {
this.fetchUserById()
},
methods: {
fetchUserById() {
getInfoByName(this.$route.params.username, this.page.current, this.page.size).then((res) => {
const { data } = res
this.topicUser = data.user
this.page.current = data.topics.current
this.page.size = data.topics.size
this.page.total = data.topics.total
this.topics = data.topics.records
})
},
handleDelete(id) {
deleteTopic(id).then(value => {
const { code, message } = value
alert(message)
if (code === 200) {
setTimeout(() => {
this.$router.push({ path: '/' })
}, 500)
}
})
}
}
}
</script>
<style scoped>
</style>
用户中心后端
@GetMapping("/{username}")
public ApiResult<Map<String, Object>> getUserByName(@PathVariable("username") String userName,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "size", defaultValue = "10") Integer size) {
Map<String, Object> map = new HashMap<>(16);
UmsUser user = umsUserService.getOne(
new LambdaQueryWrapper<UmsUser>().eq(UmsUser::getUsername, userName)
);
Assert.notNull(user, "用户不存在");
Page<BmsPost> page = postService.page(new Page<>(pageNo, size),
new LambdaQueryWrapper<BmsPost>().eq(BmsPost::getUserId, user.getId()));
map.put("user", user);
map.put("topics", page);
return ApiResult.success(map);
}
设置中心前端
API
src\api\user.js
// 用户主页(设置中心)
export function getInfo() {
return request({
url: '/auth/user/info',
method: 'get'
})
}
// 更新
export function update(user) {
return request({
url: '/auth/user/update',
method: 'post',
data: user
})
}
路由
src\router\index.js
// 设置中心
{
name: 'user-setting',
path: '/member/:username/setting',
component: () => import('@/views/user/Setting'),
meta: { title: '设置' }
}
新增Setting.vue
src\views\user\Setting.vue
<template>
<section>
<el-card shadow="never">
<div slot="header">
个人设置
</div>
<div class="columns">
<div class="column is-full">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="基础信息" name="first">
<el-form :label-position="labelPosition" label-width="100px" :model="user">
<el-form-item label="账号">
<el-input v-model="user.username" disabled />
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="user.alias" />
</el-form-item>
<el-form-item label="简介">
<el-input v-model="user.bio" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="头像" name="second">
<figure class="image is-48x48">
<img :src="`https://cn.gravatar.com/avatar/${this.user.id}?s=164&d=monsterid`">
</figure>
</el-tab-pane>
<el-tab-pane label="电子邮箱" name="third">
<el-form ref="dynamicValidateForm" :model="user" label-width="100px" class="demo-dynamic">
<el-form-item
prop="email"
label="邮箱"
:rules="[
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }
]"
>
<el-input v-model="user.email" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('dynamicValidateForm')">提交</el-button>
<el-button @click="resetForm('dynamicValidateForm')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="手机号" name="fourth">
<el-form ref="dynamicValidateForm" :model="user" label-width="100px" class="demo-dynamic">
<el-form-item>
<el-input v-model="user.mobile" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('dynamicValidateForm')">提交</el-button>
<el-button @click="resetForm('dynamicValidateForm')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
</div>
</el-card>
</section>
</template>
<script>
import {getInfo, update} from '@/api/user'
export default {
name: 'Setting',
data() {
return {
activeName: 'first',
labelPosition: 'right',
user: {
id: '',
username: '',
alias: '',
bio: '',
email: '',
mobile: '',
avatar: ''
}
}
},
created() {
this.fetchInfo()
},
methods: {
fetchInfo() {
getInfo(this.$route.params.username).then(res => {
console.log(res)
const { data } = res
this.user = data
})
},
handleClick(tab, event) {
console.log(tab, event)
},
submitForm(formName) {
console.log(this.user)
update(this.user).then(res => {
this.$message.success('信息修改成功')
this.fetchInfo()
})
},
resetForm(formName) {
this.$refs[formName].resetFields()
}
}
}
</script>
<style scoped>
</style>
设置中心后端
/**
* 设置中心跟新用户信息
* @param umsUser
* @return
*/
@PostMapping("/update")
public ApiResult<UmsUser> updateUser(@RequestBody UmsUser umsUser) {
umsUserService.updateById(umsUser);
return ApiResult.success(umsUser);
}