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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值