路由配置

加一个路由规则:

  1. const routes = [
  2. // 最大级别的规则,对应的组件,会显示在 App.vue 中
  3. // 登录
  4. { path: '/login', component: () => import('@/views/Login.vue') },
  5. // 注册
  6. { path: '/reg', component: () => import('@/views/Reg.vue') },
  7. // 后台主页
  8. {
  9. path: '/',
  10. component: () => import('@/views/Home.vue'),
  11. children: [
  12. { path: 'home', component: () => import('@/views/Chart.vue') },
  13. { path: 'user-info', component: () => import('@/views/user/UserInfo.vue') },
  14. { path: 'user-pwd', component: () => import('@/views/user/RePwd.vue') },
  15. { path: 'user-avatar', component: () => import('@/views/user/UserAvatar.vue') },
  16. { path: 'art-cate', component: () => import('@/views/article/CateList.vue') },
  17. + { path: 'art-list', component: () => import('@/views/article/ArtList.vue') }
  18. ]
  19. }
  20. ]

页面布局

<template>
  <!-- 保留一个最大的div,因为页面有卡片、有弹层等 -->
  <div>
    <el-card class="box-card">
      <div slot="header" class="clearfix">
        <span>文章列表</span>
      </div>
      <!-- 内容区 -->
      <!-- 内容区一:表单搜索区 -->
      <div class="search-box">
        <!-- inline属性,控制表单项,以水平方式排列 -->
        <el-form :inline="true" class="demo-form-inline">
          <el-form-item label="文章分类">
            <el-select placeholder="活动区域">
              <el-option label="区域一" value="shanghai"></el-option>
              <el-option label="区域二" value="beijing"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="文章状态">
            <el-select placeholder="活动区域">
              <el-option label="区域一" value="shanghai"></el-option>
              <el-option label="区域二" value="beijing"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button type="primary">查询</el-button>
            <el-button type="info">重置</el-button>
          </el-form-item>
        </el-form>
        <el-button type="primary" class="add-btn">添加文章</el-button>
      </div>
      <!-- 内容区二:表格区,展示数据 -->
      <!-- 内容区三:分页区 -->
    </el-card>
  </div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.search-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}
.add-btn {
  margin-top: 3px;
}
</style>

全屏的对话框

复制一个对话框,内容区留下一个 <span>这是一段信息</span>,其他全部删除。
对话框保留或新增的属性如下:

<!-- 添加文章的对话框 start -->
<el-dialog
  title="发布文章"
  :visible.sync="dialogVisible"
  :before-close="handleClose"
  fullscreen
>
  <span>这是一段信息</span>
</el-dialog>
<!-- 添加文章的对话框 end -->

JS中,必须设置好数据项、方法:

export default {
  data() {
    return {
      // 控制添加文章的对话显示 | 隐藏
      dialogVisible: false
    }
  },
  methods: {
    // 关闭添加对话框之前的操作
    handleClose(done) {
      // done() // 调用done()可以关闭对话框
      this.dialogVisible = false // 通过修改数据项,也可以关闭对话框
    }
  }
}

最后,点击添加按钮的时候,修改数据项,让对话框显示:

<el-button type="primary" class="add-btn" @click="dialogVisible = true">添加文章</el-button>

处理标题和分类

设置好对应的数据项和验证规则:

data() {
  return {
    // 控制添加文章的对话显示 | 隐藏
    dialogVisible: false,
    // 添加文章的数据项,用于存储添加表单中的每一项数据
    addData: {
      title: '', // 文章标题
      cate_id: '' // 下拉框的值,取值为分类id(为什么叫做 cate_id,因为接口文档的字段叫cate_id)
    },
    // 验证规则对象
    rules: {
      title: [
        { required: true, message: '标题必填', trigger: 'blur' },
        { min: 1, max: 30, message: '标题长度1~30位', trigger: ['change', 'blur'] }
      ],
      cate_id: [
        { required: true, message: '必须选择一个分类', trigger: ['blur', 'change'] },
        {
          pattern: /^[1-9][0-9]*$/,
          message: '分类id必须是大于0的整数',
          trigger: ['blur', 'change']
        }
      ]
    }
  }
}

对话框中,放表单,表单中先设置标题和选择分类这两项:

<!-- 添加的表单 start -->
<el-form label-width="80px" :model="addData" :rules="rules">
  <!-- 第一行:文章标题 -->
  <el-form-item label="文章标题" prop="title">
    <el-input v-model="addData.title" placeholder="请输入标题"></el-input>
  </el-form-item>
  <!-- 第二行:选择分类 -->
  <el-form-item label="选择分类" prop="cate_id">
    <el-select placeholder="请选择分类" style="width: 100%" v-model="addData.cate_id">
      <el-option label="北京" value="2"> </el-option>
      <el-option label="上海" value="0"> </el-option>
      <el-option label="广州" value="53"> </el-option>
    </el-select>
  </el-form-item>
</el-form>
<!-- 添加的表单 end -->

下拉框使用真实的分类

封装 获取分类的 API方法,但是,做分类页面的时候,已经封装过这个 API 方法了。
组件中,设置好数据项 cateList: [] ,设置方法,调用API获取分类,将结果复制给 cateList:

export default {
  data() {
    return {
      // ...........其他略
      cateList: []
    }
  },
  methods: {
    // 其他方法略...........................
    async initCategory() {
      const { data: res } = await getCategoryAPI()
      if (res.code === 0) {
        // 成功得到分类列表数据,把数据赋值给数据项
        this.cateList = res.data
      } else {
        this.$message.error(res.message)
      }
    }
  },
  created() {
    this.initCategory()
  }
}

最后,页面中,循环渲染下拉框即可:

<!-- 第二行:选择分类 -->
<el-form-item label="选择分类" prop="cate_id">
  <el-select placeholder="请选择分类" style="width: 100%" v-model="addData.cate_id">
    <el-option
      v-for="item in cateList"
      :key="item.id"
      :label="item.cate_name"
      :value="item.id"
    >
    </el-option>
  </el-select>
</el-form-item>

文章内容

使用富文本编辑器 quill-editor,参考网站:https://www.npmjs.com/package/vue-quill-editor

  1. 下载安装

npm install vue-quill-editor --save

  1. main.js 导入样式、js、并注册为Vue的插件 ```javascript import VueQuillEditor from ‘vue-quill-editor’

// require styles import ‘quill/dist/quill.core.css’ import ‘quill/dist/quill.snow.css’ import ‘quill/dist/quill.bubble.css’

Vue.use(VueQuillEditor)


3. 组件中,把 富文本编辑器当做子组件使用即可
```vue
<!-- 第三行:文章内容 -->
<el-form-item label="文章内容">
  <!-- 富文本编辑器 -->
  <quill-editor v-model="addData.content"></quill-editor>
</el-form-item>

<script>
  export default {
    addData: {
      title: '',
      cate_id: '',
      content: '', // 文章内容。。。。。。。。。。。。。。。。。。。。。。。
    }
  }
</script>
  1. 加一个样式,调整高度
    /deep/ .ql-editor {
    height: 240px;
    }
    

    文章封面

    布局: ```vue
    发布文章 - 图1
    选择图片

点击选择图片按钮,能够选择图片:
```vue
<el-button type="text" @click="$refs.fileRef.click()">选择图片</el-button>

文件域内容改变的时候,处理数据项和预览:

<input type="file" ref="fileRef" @change="chooseImg" style="display: none" />

JS中,设置好数据项:

data() {
  return {
    imgUrl: '', // 存储临时的url地址,准备做预览用
    addData: {
      title: '',
      cate_id: '',
      content: '',
      cover_img: '' // 接口要求的名字,值必须的文件对象格式
    }
  }
}

在文件域change方法中,设置数据项:

// 选择图片的处理方法
chooseImg(e) {
  // 判断一下,是否选择了图片
  // 文件域.files  ---- 选择的文件列表
  if (e.target.files.length > 0) {
    // 如果选择了图片,则把文件对象,加入到数据项中
    this.addData.cover_img = e.target.files[0]
    // 做预览,创建预览的url
    this.imgUrl = URL.createObjectURL(e.target.files[0])
  } else {
    // 说明取消选择图片
    this.addData.cover_img = ''
    this.imgUrl = ''
  }
}

控制页面的预览图片:

<img :src="imgUrl" class="cover-img" alt="" v-if="imgUrl" />
<img src="@/assets/images/cover.jpg" class="cover-img" alt="" v-else />

处理文章状态

点击发布、存为草稿,都是提交数据的意思。
只不过,点击发布,让文章的状态为”已发布”;点击存为草稿,让文章的状态是 ”草稿”
先加好数据项:

data() {
  return {
    addData: {
      title: '',
      cate_id: '',
      content: '',
      cover_img: '',
      state: '' // 文章状态,可选已发布、草稿
    }
  }
}

页面中,设置最后一行,放两个按钮,点击事件加好:

<!-- 第五行:按钮 -->
<el-form-item>
  <el-button type="primary" @click="publish('已发布')">发布</el-button>
  <el-button type="info" @click="publish('草稿')">存为草稿</el-button>
</el-form-item>

补充 publish方法,设置文章的状态:

// 发布文章方法
publish(s) {
  // s 就是点击按钮的时候,传递过来的 已发布 或 草稿
  this.addData.state = s
}

最后,通过 vue 调试工具,查看这几项数据是否有问题。调试工具如果没有反应过来,手动点击刷新。

最后的发布

封装API方法(api/article.js):

import request from '@/utils/request'

export const addArticleAPI = fd => {
  // return request.post('/my/article/add', fd)
  return request({
    method: 'POST',
    url: '/my/article/add',
    data: fd
  })
}

组件中,加载API方法:

// 按需导入 添加文章的 API 方法
import { addArticleAPI } from '@/api/article'

publish方法中,将数据项,转成 FormData 格式,然后提交给接口:

// 发布文章方法
publish(s) {
  // s 就是点击按钮的时候,传递过来的 已发布 或 草稿
  this.addData.state = s
  // 下面进行完整的验证
  this.$refs.addForm.validate(async valid => {
    if (!valid) return
    // 手动验证文章内容和封面图片
    if (this.addData.content === '') return this.$message.error('请输入内容')
    if (this.addData.cover_img === '') return this.$message.error('请选择图片')
    // 提交数据
    // console.log(this.addData)
    // 创建 FormData 对象
    const fd = new FormData()
    for (const key in this.addData) {
      // key 就是 cate_id/ content/ cover_img/ state/ title
      fd.append(key, this.addData[key])
    }
    // Ajax 提交 fd 对象即可
    const { data: res } = await addArticleAPI(fd)
    if (res.code === 0) {
      this.$message.success(res.message)
      // 添加成功,关闭对话框
      this.dialogVisible = false
      // 重置表单
      this.$refs.addForm.resetFields()
      this.addData.content = ''
      this.addData.cover_img = ''
      this.imgUrl = ''
      // 重新获取列表数据,重新渲染。。。。。。
    } else {
      this.$message.error(res.message)
    }
  })
}