06发布文章

01 发布文章-页面组件和路由

目标

  • 准备发表文章的页面组件并配置路由显示

讲解

  1. 在 src/views/article/artList.vue组件, ==直接复制标签==

:inline="true" 不加: 是字符串, 加: 是布尔值

条件查询用【行内表单】去实现
<template>
  <div>
    <el-card class="box-card">
      <div slot="header" class="clearfix">
        <span>文章列表</span>
      </div>
      <!-- 搜索区域 -->
      <div class="search-box">
        <el-form :inline="true" :model="q">
          <el-form-item label="文章分类">
            <el-select v-model="q.cate_id" placeholder="请选择分类" size="small">
              <el-option label="区域一" value="shanghai"></el-option>
              <el-option label="区域二" value="beijing"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="发布状态" style="margin-left: 15px;">
            <el-select v-model="q.state" placeholder="请选择状态" size="small">
              <el-option label="已发布" value="已发布"></el-option>
              <el-option label="草稿" value="草稿"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" size="small">筛选</el-button>
            <el-button type="info" size="small">重置</el-button>
          </el-form-item>
        </el-form>
        <!-- 发表文章的按钮 -->
        <el-button type="primary" size="small" class="btn-pub">发表文章</el-button>
      </div>
      <!-- 文章表格区域 -->
      <!-- 分页区域 -->
    </el-card>
  </div>
</template>
<script>
export default {
  name: 'ArtList',
  data() {
    return {
      // 查询参数对象
      q: {
        pagenum: 1,
        pagesize: 2,
        cate_id: '',
        state: ''
      }
    }
  }
}
</script>
<style lang="less" scoped>
.search-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  .btn-pub {
    margin-top: 5px;
  }
}
</style>
  1. 在 src/router/index.js路由模块中,导入并组件并声明路由规则如下

{
    path: 'art-list', // 文章列表
    component: () => import('@/views/article/artList')
}

02发表文章-对话框组件

目标

  • 准备发表文章的对话框组件

讲解

  1. 在 artList.vue组件中,声明发表文章的对话框

<!-- 发表文章的 Dialog 对话框 -->
<el-dialog title="发表文章" :visible.sync="pubDialogVisible" fullscreen :before-close="handleClose">
  <span>这是一段信息</span>
</el-dialog>

  1. 在 data 中定义布尔值 pubDialogVisible,用来控制对话框的显示与隐藏

data() {
  return {
    // ...其他
    pubDialogVisible: false // 控制发表文章对话框的显示与隐藏
  }
}
  1. 点击发布按钮,展示对话框

  1. 定义事件处理函数如下

methods: {
    // 发表文章按钮->点击事件->让添加文章对话框出现
    showPubDialogFn () {
        this.pubDialogVisible = true
    }
}
  1. 在对话框将要关闭时,询问用户是否确认关闭对话框

// 对话框关闭前的回调
async handleClose (done) {
    // 询问用户是否确认关闭对话框
    const confirmResult = await this.$confirm('此操作将导致文章信息丢失, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
    }).catch(err => err)
    // 取消了关闭-阻止住, 什么都不干
    if (confirmResult === 'cancel') return
    // 确认关闭
    done()
}

小结

  1. this.$confirm的返回值是Promise对象, 如何提取成功结果?

答案

  1. await后面的catch又是怎么回事?

答案

03发表文章-对话框内表单

目标

  • 准备好, 对话框内,发表文章用的表单, 以及数据绑定和规则对象

  • 并渲染文章分类下拉菜单数据

讲解

  1. 初步定义表单的 UI 结构

<!-- 发布文章的对话框 -->
<el-form :model="pubForm" :rules="pubFormRules" ref="pubFormRef" label-width="100px">
  <el-form-item label="文章标题" prop="title">
    <el-input v-model="pubForm.title" placeholder="请输入标题"></el-input>
  </el-form-item>
  <el-form-item label="文章分类" prop="cate_id">
    <el-select v-model="pubForm.cate_id" placeholder="请选择分类" style="width: 100%;">
      <el-option label="区域一" value="shanghai"></el-option>
      <el-option label="区域二" value="beijing"></el-option>
    </el-select>
  </el-form-item>
</el-form>

  1. 在 data 中定义数据对象和验证规则对象:

data() {
  return {
    // ...其他
    pubForm: { // 表单的数据对象
      title: '',
      cate_id: ''
    },
    pubFormRules: { // 表单的验证规则对象
      title: [
        { required: true, message: '请输入文章标题', trigger: 'blur' },
        { min: 1, max: 30, message: '文章标题的长度为1-30个字符', trigger: 'blur' }
      ],
      cate_id: [{ required: true, message: '请选择文章标题', trigger: 'blur' }]
    }
  }
}
  1. 在 methods 中,声明初始化文章分类列表数据的方法

// 初始化文章分类的列表数据
async initCateList() {
  const { data: res } = await this.$http.get('/my/cate/list')
  if (res.code === 0) {
    this.cateList = res.data
  }
}
在 data 中声明 cateList 数组
data() {
  return {
    // ...其他
    cateList: [] // 文章分类
  }
}
  1. 在 created 生命周期函数中,调用步骤1声明的方法

created() {
  this.initCateList()
},
  1. 循环渲染文章分类的可选项

<el-form-item label="文章分类" prop="cate_id">
  <el-select v-model="pubForm.cate_id" placeholder="请选择分类" style="width: 100%;">
    <!-- 循环渲染分类的可选项 -->
    <el-option :label="item.cate_name" :value="item.id" v-for="item in cateList" :key="item.id">
    </el-option>
  </el-select>
</el-form-item>

4 发表文章-富文本

目标

  • 准备富文本编辑器

富文本: 就是可以带标签和样式的字符串, 一般用于设置一个产品/文章的主体内容

文档可以在npm的网页中搜索

讲解

基于 vue-quill-editor 实现富文本编辑器:https://www.npmjs.com/package/vue-quill-editor

  1. 运行如下的命令,在项目中安装富文本编辑器:

yarn add vue-quill-editor
  1. 在项目入口文件 main.js中导入并全局注册富文本编辑器

当然, 你也可以单独封装模块写这些代码后, 引入到main.js即可

// 导入富文本编辑器
import VueQuillEditor from 'vue-quill-editor'
// 导入富文本编辑器的样式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
​
// 全局注册富文本编辑器
Vue.use(VueQuillEditor)
  1. 在 views/article/artList.vue组件的 data 中,定义富文本编辑器对应的数据项, 也绑定到表单的数据对象属性中

data() {
  return {
    // ...其他
    // 表单的数据对象
    pubForm: {
      title: '',
      cate_id: '',
+     content: '' // 文章的内容
    },
    // ...其他
  }
}
  1. 在 artList.vue组件的模板结构中,添加富文本编辑器的 DOM 结构, 给el-form内新增一块表单域

<el-form-item label="文章内容">
  <!-- 使用 v-model 进行双向的数据绑定 -->
  <quill-editor v-model="pubForm.content"></quill-editor>
</el-form-item>
  1. 美化富文本编辑器的样式:

// 设置富文本编辑器的默认最小高度
// ::v-deep作用: 穿透选择, 正常style上加了scope的话, 会给.ql-editor[data-v-hash]属性, 只能选择当前页面标签或者组件的根标签
// 如果想要选择组件内的标签(那些标签没有data-v-hash值)所以正常选择选不中, 加了::v-deep空格前置的话, 选择器就会变成如下形式
// [data-v-hash] .ql-editor 这样就能选中组件内的标签的class类名了
::v-deep .ql-editor {
  min-height: 300px;
}
  1. 再给文章内容部分, 绑定el-form的表单校验, 在el-form-item加上同名的prop属性

<el-form-item label="文章内容" prop="content">
    <!-- 使用 v-model 进行双向的数据绑定 -->
    <quill-editor v-model="pubForm.content"></quill-editor>
</el-form-item>
  1. 在rules对应的规则对象中, 添加校验规则

content: [
    { required: true, message: '请输入文章内容', trigger: 'blur' }
]

05 发表文章-封面标签


目标

  • 渲染文章封面区域标签

  • 实现用户选择封面功能, 拿到用户选择的图片文件

讲解

  1. 在 artList.vue组件的模板结构中,添加文章封面对应的 DOM 结构:

<el-form-item label="文章封面">
  <!-- 用来显示封面的图片 -->
  <img src="../../assets/images/cover.jpg" alt="" class="cover-img" ref="imgRef" />
  <br />
  <!-- 文件选择框,默认被隐藏 -->
  <input type="file" style="display: none;" accept="image/*" ref="iptFileRef" />
  <!-- 选择封面的按钮 -->
  <el-button type="text">+ 选择封面</el-button>
</el-form-item>
  1. 美化封面图片的样式:

// 设置图片封面的宽高
.cover-img {
  width: 400px;
  height: 280px;
  object-fit: cover;
}
  1. 选择封面的按钮绑定点击事件处理函数

  1. 模拟文件选择框的点击事件

chooseImgFn() {
  this.$refs.iptFileRef.click()
}
  1. 监听文件选择框change 事件

<!-- 文件选择框,默认被隐藏 -->
<input type="file" style="display: none;" ref="iptFile" accept="image/*" @change="onCoverChangeFn" />
  1. 定义 onCoverChange 处理函数如下

// 封面选择改变的事件
onCoverChangeFn (e) {
    // 获取用户选择的文件列表
    const files = e.target.files
    if (files.length === 0) {
        // 用户没有选择封面
        this.pubForm.cover_img = null
    } else {
        // 用户选择了封面
        this.pubForm.cover_img = files[0]
    }
}
  1. 在 data 中的 pubForm对象上,声明 cover_img属性,用来存储用户选择的封面

data() {
  return {
    // 表单的数据对象
    pubForm: {
      title: '',
      cate_id: '',
      content: '',
+     cover_img: null // 用户选择的封面图片(null 表示没有选择任何封面)
    },
  }
}

小结

  1. 封面标签选择图片的思路是?

答案

  1. 发表文章-封面图片


目标

  • 给封面图片标签做预览效果

  • 如果用户未选择图片, 给一张默认图片

讲解

  1. 在 artList.vue组件中,导入默认的封面图片

// 导入默认的封面图片
import defaultImg from '@/assets/images/cover.jpg'
  1. 在文件选择框的 change事件处理函数中,根据用户是否选择了封面,动态设置封面图片的 src 地址:

// 监听文件选择框的 change 事件
onCoverChange(e) {
  // 获取到用户选择的封面
  const files = e.target.files
  if (files.length === 0) {
    // 用户没有选择封面
    this.pubForm.cover_img = null
+   this.$refs.imgRef.setAttribute('src', defaultImg)
  } else {
    // 用户选择了封面
    this.pubForm.cover_img = files[0]
+   const url = URL.createObjectURL(files[0])
+   this.$refs.imgRef.setAttribute('src', url)
  }
}

06发表文章-状态


目标

  • 设置发布文章状态的表单

讲解

  1. 在 data 中的 pubForm 对象上,定义 state属性,用来存储文章的发布状态

data() {
  return {
    // 表单的数据对象
    pubForm: {
      title: '',
      cate_id: '',
      content: '',
      cover_img: null,
+     state: '' // 文章的发布状态,可选值有两个:草稿、已发布
    },
  }
}
  1. 在el-form最后, 再准备发布存为草稿按钮,并绑定点击事件处理函数

<el-form-item>
    <el-button type="primary" @click="pubArticleFn('已发布')">发布</el-button>
    <el-button type="info" @click="pubArticleFn('草稿')">存为草稿</el-button>
</el-form-item>
  1. 在 methods 中声明 pubArticle处理函数如下

// 发布文章或草稿-按钮点击事件
pubArticleFn (state) {
    // 1. 设置发布状态
    this.pubForm.state = state
    // 2. 表单预校验
    this.$refs.pubFormRef.validate(valid => {
        if (!valid) return this.$message.error('请完善文章信息!')
        // 3. 判断是否提供了文章封面
        if (!this.pubForm.cover_img) return this.$message.error('请选择文章封面!')
        // 4. TODO:发布文章
        console.log(this.pubForm)
    })
}

两个按钮用了一个方法,通过什么来区分是哪个按钮那?

传递的参数 this.pubForm.state = state

3.1 后台好多都是必填的,所以点击按钮时候要进行校验!!!!

按以前的elemntui 的form表单校验,只有input是好使的,其他都不好使

2 下拉菜单不能失去焦点, 下拉菜单应该是change。

3 富文本:

修改:

4封面的校验

// 发布文章或草稿-按钮点击事件
pubArticleFn (state) {
    // 1. 设置发布状态
    this.pubForm.state = state
    // 2. 表单预校验
    this.$refs.pubFormRef.validate(valid => {
        if (!valid) return this.$message.error('请完善文章信息!')
        // 3. 判断是否提供了文章封面
        if (!this.pubForm.cover_img) return this.$message.error('请选择文章封面!')
        // 4. TODO:发布文章
        console.log(this.pubForm)
    })
}
  1. 在对话框完全关闭之后,清空表单的关键数据:

<!-- 发表文章的 Dialog 对话框 -->
<el-dialog
  title="发表文章"
  :visible.sync="pubDialogVisible"
  fullscreen
  :before-close="handleClose"
  @closed="onDialogClosedFn"
>
  <!-- 省略其它代码 -->
</el-dialog>

  1. 在 methods 中声明 onDialogClosed函数如下:

// 对话框完全关闭之后的处理函数
onDialogClosedFn () {
    // 清空关键数据
    this.$refs.pubFormRef.resetFields()
    // 因为这2个变量对应的标签不是表单绑定的, 所以需要单独控制
    this.pubForm.content = ''
    this.$refs.imgRef.setAttribute('src', defaultImg)
}

07发表文章-完成功能


目标

  • 把前面准备好的数据对象, 调用接口保存在后台

讲解

  1. 查看接口文档, 封装发布文章的接口方法

/**
 * 发布文章
 * @param {*} fd 表单对象
 * @returns Promise对象
 */
export const uploadArticleAPI = (fd) => {
  return request({
    url: '/my/article/add',
    method: 'POST',
    data: fd // 参数要的是表单对象, 不能写普通对象, axios内部会判断, 如果是表单对象, 传递的请求体会设置Content-Type: form-data与后端对应
  })
}
  1. 在发布/存草稿按钮, 点击事件中调用接口, 组织好参数传递

// 创建 FormData 对象
const fd = new FormData()
// 向 FormData 中追加数据
Object.keys(this.pubForm).forEach((key) => {
    fd.append(key, this.pubForm[key])
})
// 发起请求
const { data: res } = await uploadArticleAPI(fd)
if (res.code !== 0) return this.$message.error('发布文章失败!')
this.$message.success('发布文章成功!')
// 关闭对话框
this.pubDialogVisible = false
// TODO:刷新文章列表数据

猜你喜欢

转载自blog.csdn.net/qq_38210427/article/details/129640521#comments_28841227