本节:文章的列表,添加文章,修改文章,删除文章

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>