• 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:这个框架定义了一个组件的跳转。

    文章的列表,添加文字,修改文章,删除文章的页面全部代码:

    1. <template>
    2. <div>
    3. <n-tabs v-model:value="tabValue" justify-content="start" type="line">
    4. <n-tab-pane name="list" tab="文章列表">
    5. <!-- v-for这里的括号写错了 -->
    6. <div v-for="(ii,index) in blogList" style="margin-bottom:15px">
    7. <n-card :title="ii.title">
    8. {{ii.content}}
    9. <template #footer>
    10. <n-space align="center">
    11. <div>
    12. 发布时间:{{ii.create_time}}
    13. </div>
    14. <n-button @click="toUpdate(ii)">修改</n-button>
    15. <n-button @click="toDelete(ii)">删除</n-button>
    16. </n-space>
    17. </template>
    18. </n-card>
    19. </div>
    20. <n-space>
    21. <div @click=" toPage(Number)" v-for="Number in pageInfo.pageCount">
    22. <div :style="'color:'+(Number == pageInfo.page? 'pink' : '')">{{Number}}</div>
    23. </div>
    24. </n-space>
    25. </n-tab-pane>
    26. <n-tab-pane name="add" tab="添加文章">
    27. <n-form>
    28. <n-form-item label="标题">
    29. <n-input v-model:value="addArticle.title" placeholder="请输入标题" />
    30. </n-form-item>
    31. <n-form-item label="文章分类">
    32. <n-select v-model:value="addArticle.categoryId" :options="categoryOptions" />
    33. </n-form-item>
    34. <n-form-item label="内容">
    35. <rich-text-editor v-model="addArticle.content"></rich-text-editor>
    36. </n-form-item>
    37. <n-form-item label="">
    38. <n-button @click="add">提交</n-button>
    39. </n-form-item>
    40. </n-form>
    41. </n-tab-pane>
    42. <n-tab-pane name="update" tab="修改">
    43. <n-form>
    44. <n-form-item label="标题">
    45. <n-input v-model:value="updateArticle.title" placeholder="请输入标题" />
    46. </n-form-item>
    47. <n-form-item label="文章分类">
    48. <n-select v-model:value="updateArticle.categoryId" :options="categoryOptions" />
    49. </n-form-item>
    50. <n-form-item label="内容">
    51. <rich-text-editor v-model="updateArticle.content"></rich-text-editor>
    52. </n-form-item>
    53. <n-form-item label="">
    54. <n-button @click="update">提交</n-button>
    55. </n-form-item>
    56. </n-form>
    57. </n-tab-pane>
    58. </n-tabs>
    59. </div>
    60. </template>
    61. <script setup>
    62. import { ref, reactive, inject, onMounted } from 'vue'
    63. import { AdminStore } from '../../stores/AdminStore' //1.引入
    64. import { useRouter, useRoute } from 'vue-router'; //路由
    65. import RichTextEditor from "../../components/RichTextEditor.vue"
    66. const router = useRouter()
    67. const route = useRoute()
    68. const dialog = inject("dialog")
    69. const message = inject("message") // inject注入,可以引入我全局定义好的工具是
    70. const axios = inject("axiosTool") //axiosTool 是我provide定义的名字
    71. const adminStore = AdminStore(); //2.实例化
    72. const addArticle = reactive({
    73. title: "",
    74. content: "",
    75. categoryId: 0
    76. })
    77. const updateArticle = reactive({
    78. id: 0,
    79. title: "",
    80. content: "",
    81. categoryId: 0
    82. })
    83. const tabValue = ref("list")
    84. const categoryOptions = ref([])
    85. const blogList = ref([])
    86. const pageInfo = reactive({
    87. page: 1,
    88. pageSize: 3,
    89. pageCount: 0,
    90. count: 0
    91. })
    92. onMounted(() => {
    93. loadBlogs()
    94. loadCategorys()
    95. })
    96. // 获取分类
    97. const loadCategorys = async () => {
    98. let res = await axios.get("/categorty/list")
    99. // map()可以把对象,转换成[key,vaule]的数组形式
    100. categoryOptions.value = res.data.rows.map((i) => {
    101. return {
    102. label: i.name,
    103. value: i.id
    104. }
    105. })
    106. }
    107. // 获取博客
    108. const loadBlogs = async () => {
    109. let res = await axios.get(`/blog/search?page=${pageInfo.page}&pageSize=${pageInfo.pageSize}`)
    110. let temp_rows = res.data.data.rows;
    111. for (let jj of temp_rows) {
    112. jj.content += "..."
    113. let d = new Date(jj.create_time)
    114. jj.create_time = `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日`
    115. }
    116. blogList.value = temp_rows;
    117. pageInfo.count = res.data.data.count
    118. // parseInt向下取整,就是不算小数点, ,%是取余数
    119. pageInfo.pageCount = parseInt(pageInfo.count / pageInfo.pageSize) + (pageInfo.count % pageInfo.pageSize > 0 ? 1 : 0)
    120. }
    121. const add = async () => {
    122. // 提交的内容要和后端接受的变量一样
    123. let res = await axios.post("/blog/add", addArticle)
    124. // { headers: { token: adminStore.token } }
    125. if (res.data.code == 200) {
    126. message.info(res.data.msg)
    127. } else {
    128. sage.error(res.data.msg)
    129. }
    130. }
    131. const toPage = async (Number) => {
    132. pageInfo.page = Number
    133. loadBlogs();
    134. }
    135. const toUpdate = async (ii) => {
    136. tabValue.value = "update"
    137. let res = await axios.get("/blog/detail?id=" + ii.id)
    138. console.log(res);
    139. updateArticle.id = res.data.rows[0].id;
    140. updateArticle.title = res.data.rows[0].title;
    141. updateArticle.content = res.data.rows[0].content;
    142. updateArticle.categoryId = res.data.rows[0].category_id;
    143. }
    144. const update = async () => {
    145. // 提交的内容要和后端接受的变量一样
    146. let res = await axios.put("/blog/_token/update", updateArticle)
    147. // { headers: { token: adminStore.token } }
    148. if (res.data.code == 200) {
    149. message.info(res.data.msg)
    150. tabValue.value = "list"
    151. loadBlogs();
    152. } else {
    153. sage.error(res.data.msg)
    154. }
    155. }
    156. const toDelete = async (ii) => {
    157. dialog.warning({
    158. title: '警告',
    159. content: '是否要删除',
    160. positiveText: '确定',
    161. negativeText: '取消',
    162. onPositiveClick: async () => {
    163. let res = await axios.delete("/blog/_token/delete?id=" + ii.id)
    164. if (res.data.code == 200) {
    165. loadBlogs()
    166. message.info(res.data.msg)
    167. } else {
    168. message.error(res.data.msg)
    169. }
    170. },
    171. })
    172. }
    173. </script>
    174. <style lang="scss" scoped>
    175. </style>

    富文本框的代码:

    1. <!-- 富文本组件 -->
    2. <template>
    3. <div>
    4. <Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" style="border-bottom: 1px solid #ccc" />
    5. <Editor :defaultConfig="editorConfig" :mode="mode" v-model="valueHtml" style="height: 400px;
    6. overflow-y: hidden" @onCreated="handleCreated" @onChange="handleChange" />
    7. </div>
    8. </template>
    9. <script setup>
    10. import '@wangeditor/editor/dist/css/style.css';
    11. import { ref, reactive, inject, onMounted, onBeforeUnmount, shallowRef } from 'vue'
    12. import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
    13. const server_url = inject("server_url")
    14. // 编辑器实例,必须用 shallowRef,重要!
    15. const editorRef = shallowRef();
    16. // toolbarConfig排除掉一些功能
    17. const toolbarConfig = { excludeKeys: ["uploadVideo"] };
    18. const editorConfig = { placeholder: '请输入内容...' };
    19. editorConfig.MENU_CONF = {}
    20. // 指定上传的地址
    21. editorConfig.MENU_CONF['uploadImage'] = {
    22. base64LimitSize: 10 * 1024, // 10kb如果图片比较小可以这样上传
    23. server: server_url + "/upload/rich_upload",
    24. }
    25. // 插入图片
    26. editorConfig.MENU_CONF['insertImage'] = {
    27. parseImageSrc: (src) => { //插入图片之前会执行的函数
    28. console.log(src, "图片");
    29. if (src.indexOf("http") !== 0) { //判断src是否包含http
    30. return `${server_url}${src}`
    31. }
    32. return src
    33. }
    34. }
    35. const mode = ref("default")
    36. const valueHtml = ref("")//和上面是双绑
    37. const props = defineProps({
    38. modelValue: {
    39. type: String,
    40. default: ""
    41. }
    42. })
    43. const emit = defineEmits(["update:model-value"])
    44. let initFinished = false
    45. onMounted(() => {
    46. setTimeout(() => {
    47. valueHtml.value = props.modelValue;//父页面传过来的值
    48. initFinished = true; //这个只是一个变量名,不需要渲染到页面上面,只是处理逻辑的变量名
    49. }, 10);
    50. });
    51. // 组件销毁时,也及时销毁编辑器,重要!
    52. onBeforeUnmount(() => {
    53. const editor = editorRef.value;
    54. if (editor == null) return;
    55. editor.destroy();
    56. });
    57. // 编辑器回调函数
    58. const handleCreated = (editor) => {
    59. editorRef.value = editor; // 记录 editor 实例,重要!
    60. };
    61. const handleChange = (editor) => {
    62. if (initFinished) {
    63. emit("update:model-value", valueHtml.value)//valueHtml抛的上面绑定的内容
    64. }
    65. };
    66. </script>
    67. <style lang="scss" scoped>
    68. </style>

  • 相关阅读:
    【模电实验】【精简版】【验证性实验——两级阻容耦合负反馈放大器实验】
    寻找数组中最接近目标的数字
    数商云:数字化供应链系统搭建,赋能企业实现物流供应链的优化升级
    ARouter拦截器使用
    什么是Vue?什么又是vue指令?
    使用Vue+CSS实现汉堡图标过渡为叉号图标,有点意思
    MVC与三层架构
    【深度学习】【三维重建】windows10环境配置tiny-cuda-nn详细教程
    rocketmq-console-1.0.0启动报错
    Camera Hal OEM模块 ---- cmr_preview.c
  • 原文地址:https://blog.csdn.net/m0_59214979/article/details/126777526