本节:文章的列表,添加文章,修改文章,删除文章
div:
一、文章列表 :循环出内容,手写分页功能步骤: (1)循环出总页数 {{页数}} (2)点击进行分页切换
二、添加、修改文章 (1)绑定变量 (2)点击方法
script: 1.引入模块 2.实例化引入的模块
1.获取博客列表、博客分类的方法,进行挂载,用来接收后端的数据都要先定义好空间。
2.添加博客 添加方法:(1)定义变量接收前端输入的数据 (2)定义方法,调用接口,把接收到的数据传给后端,并弹出后端返回的内容。
3.修改博客
1.跳到修改的页面,调用接口,获取当篇博客数据,进行赋值
2.提交修改方法 调用接口,传修改好的数据,如果后端返回200,则跳到列表页面,重新调用获取列表的数据。接收用户修改的数据变量空间要先定义好。
4.删除博客,调用接口,传id给后端, 就可以删除数据了。这里是组件的弹出确认的模态框。
5.添加页面路由:
{ path: "/dashboard/article", component: () => import('../views/dashboard/Article.vue') },
PS1: 1.注入axios,服务器地址,这样其他地方只要调用那个 注定义的名字就可以使用了。
AdminStore全局变量,存放token。
在页面使用的方法:1.引入inject模块
2.实例化引入的工具 : const 自定义工具名 =inject("main.js文件定义的工具名")
PS2:这个框架定义了一个组件的跳转。
文章的列表,添加文字,修改文章,删除文章的页面全部代码:
<template>
<div>
<n-tabs v-model:value="tabValue" justify-content="start" type="line">
<n-tab-pane name="list" tab="文章列表">
<!-- v-for这里的括号写错了 -->
<div v-for="(ii,index) in blogList" style="margin-bottom:15px">
<n-card :title="ii.title">
{{ii.content}}
<template #footer>
<n-space align="center">
<div>
发布时间:{{ii.create_time}}
</div>
<n-button @click="toUpdate(ii)">修改</n-button>
<n-button @click="toDelete(ii)">删除</n-button>
</n-space>
</template>
</n-card>
</div>
<n-space>
<div @click=" toPage(Number)" v-for="Number in pageInfo.pageCount">
<div :style="'color:'+(Number == pageInfo.page? 'pink' : '')">{{Number}}</div>
</div>
</n-space>
</n-tab-pane>
<n-tab-pane name="add" tab="添加文章">
<n-form>
<n-form-item label="标题">
<n-input v-model:value="addArticle.title" placeholder="请输入标题" />
</n-form-item>
<n-form-item label="文章分类">
<n-select v-model:value="addArticle.categoryId" :options="categoryOptions" />
</n-form-item>
<n-form-item label="内容">
<rich-text-editor v-model="addArticle.content"></rich-text-editor>
</n-form-item>
<n-form-item label="">
<n-button @click="add">提交</n-button>
</n-form-item>
</n-form>
</n-tab-pane>
<n-tab-pane name="update" tab="修改">
<n-form>
<n-form-item label="标题">
<n-input v-model:value="updateArticle.title" placeholder="请输入标题" />
</n-form-item>
<n-form-item label="文章分类">
<n-select v-model:value="updateArticle.categoryId" :options="categoryOptions" />
</n-form-item>
<n-form-item label="内容">
<rich-text-editor v-model="updateArticle.content"></rich-text-editor>
</n-form-item>
<n-form-item label="">
<n-button @click="update">提交</n-button>
</n-form-item>
</n-form>
</n-tab-pane>
</n-tabs>
</div>
</template>
<script setup>
import { ref, reactive, inject, onMounted } from 'vue'
import { AdminStore } from '../../stores/AdminStore' //1.引入
import { useRouter, useRoute } from 'vue-router'; //路由
import RichTextEditor from "../../components/RichTextEditor.vue"
const router = useRouter()
const route = useRoute()
const dialog = inject("dialog")
const message = inject("message") // inject注入,可以引入我全局定义好的工具是
const axios = inject("axiosTool") //axiosTool 是我provide定义的名字
const adminStore = AdminStore(); //2.实例化
const addArticle = reactive({
title: "",
content: "",
categoryId: 0
})
const updateArticle = reactive({
id: 0,
title: "",
content: "",
categoryId: 0
})
const tabValue = ref("list")
const categoryOptions = ref([])
const blogList = ref([])
const pageInfo = reactive({
page: 1,
pageSize: 3,
pageCount: 0,
count: 0
})
onMounted(() => {
loadBlogs()
loadCategorys()
})
// 获取分类
const loadCategorys = async () => {
let res = await axios.get("/categorty/list")
// map()可以把对象,转换成[key,vaule]的数组形式
categoryOptions.value = res.data.rows.map((i) => {
return {
label: i.name,
value: i.id
}
})
}
// 获取博客
const loadBlogs = async () => {
let res = await axios.get(`/blog/search?page=${pageInfo.page}&pageSize=${pageInfo.pageSize}`)
let temp_rows = res.data.data.rows;
for (let jj of temp_rows) {
jj.content += "..."
let d = new Date(jj.create_time)
jj.create_time = `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日`
}
blogList.value = temp_rows;
pageInfo.count = res.data.data.count
// parseInt向下取整,就是不算小数点, ,%是取余数
pageInfo.pageCount = parseInt(pageInfo.count / pageInfo.pageSize) + (pageInfo.count % pageInfo.pageSize > 0 ? 1 : 0)
}
const add = async () => {
// 提交的内容要和后端接受的变量一样
let res = await axios.post("/blog/add", addArticle)
// { headers: { token: adminStore.token } }
if (res.data.code == 200) {
message.info(res.data.msg)
} else {
sage.error(res.data.msg)
}
}
const toPage = async (Number) => {
pageInfo.page = Number
loadBlogs();
}
const toUpdate = async (ii) => {
tabValue.value = "update"
let res = await axios.get("/blog/detail?id=" + ii.id)
console.log(res);
updateArticle.id = res.data.rows[0].id;
updateArticle.title = res.data.rows[0].title;
updateArticle.content = res.data.rows[0].content;
updateArticle.categoryId = res.data.rows[0].category_id;
}
const update = async () => {
// 提交的内容要和后端接受的变量一样
let res = await axios.put("/blog/_token/update", updateArticle)
// { headers: { token: adminStore.token } }
if (res.data.code == 200) {
message.info(res.data.msg)
tabValue.value = "list"
loadBlogs();
} else {
sage.error(res.data.msg)
}
}
const toDelete = async (ii) => {
dialog.warning({
title: '警告',
content: '是否要删除',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
let res = await axios.delete("/blog/_token/delete?id=" + ii.id)
if (res.data.code == 200) {
loadBlogs()
message.info(res.data.msg)
} else {
message.error(res.data.msg)
}
},
})
}
</script>
<style lang="scss" scoped>
</style>
富文本框的代码:
<!-- 富文本组件 -->
<template>
<div>
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" style="border-bottom: 1px solid #ccc" />
<Editor :defaultConfig="editorConfig" :mode="mode" v-model="valueHtml" style="height: 400px;
overflow-y: hidden" @onCreated="handleCreated" @onChange="handleChange" />
</div>
</template>
<script setup>
import '@wangeditor/editor/dist/css/style.css';
import { ref, reactive, inject, onMounted, onBeforeUnmount, shallowRef } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
const server_url = inject("server_url")
// 编辑器实例,必须用 shallowRef,重要!
const editorRef = shallowRef();
// toolbarConfig排除掉一些功能
const toolbarConfig = { excludeKeys: ["uploadVideo"] };
const editorConfig = { placeholder: '请输入内容...' };
editorConfig.MENU_CONF = {}
// 指定上传的地址
editorConfig.MENU_CONF['uploadImage'] = {
base64LimitSize: 10 * 1024, // 10kb如果图片比较小可以这样上传
server: server_url + "/upload/rich_upload",
}
// 插入图片
editorConfig.MENU_CONF['insertImage'] = {
parseImageSrc: (src) => { //插入图片之前会执行的函数
console.log(src, "图片");
if (src.indexOf("http") !== 0) { //判断src是否包含http
return `${server_url}${src}`
}
return src
}
}
const mode = ref("default")
const valueHtml = ref("")//和上面是双绑
const props = defineProps({
modelValue: {
type: String,
default: ""
}
})
const emit = defineEmits(["update:model-value"])
let initFinished = false
onMounted(() => {
setTimeout(() => {
valueHtml.value = props.modelValue;//父页面传过来的值
initFinished = true; //这个只是一个变量名,不需要渲染到页面上面,只是处理逻辑的变量名
}, 10);
});
// 组件销毁时,也及时销毁编辑器,重要!
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
// 编辑器回调函数
const handleCreated = (editor) => {
editorRef.value = editor; // 记录 editor 实例,重要!
};
const handleChange = (editor) => {
if (initFinished) {
emit("update:model-value", valueHtml.value)//valueHtml抛的上面绑定的内容
}
};
</script>
<style lang="scss" scoped>
</style>