Advertisement

VUE3-博客全栈 06-前端

阅读量:

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

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>
    
    
    
    

全部评论 (0)

还没有任何评论哟~