vue3:十八、内容管理-表单完善

一、实现效果

1、新增页面

展示默认选中信息,缩略图上传裁剪,标签新增,富文本编辑器
在这里插入图片描述

2、编辑页面

同新增,在其基础上增加了默认值的展示
在这里插入图片描述

二、表单初始搭建

1、计算获取表单数据

使用计算属性获取表单选项-由于 statusArray 和 categoryData 是响应式引用(ref),直接使用 .value 会导致初始渲染时数据为空,而后续数据更新不会自动反映到表单中。
在这里插入图片描述

2、写入表单基本信息

(1)必填数据

在这里插入图片描述

(2)选填数据

在这里插入图片描述

(3)给新增的窗口一个默认值

发现在给表单默认值的时候,新增窗口的默认值赋予的是{},所以到值新增时状态不会自动勾选(这里就以需要的状态默认为例)
在这里插入图片描述
在主页面表单数据中,加入一个字段defaultData,专门用于存储每项的默认值
在这里插入图片描述

在Table组件中,对含有默认值的进行循环处理,并赋值给表单数据
在这里插入图片描述

三、封面图完善

1、增加缩略图

(1)设置封面图的类型为type

在这里插入图片描述

(2)设置缩略图类型

表单中引入缩略图组件,并且加入新类型cropper
在这里插入图片描述

(3)修改原样式

由于之前定义的样式会影响现在的样式,所以需要去掉,由父决定
在这里插入图片描述
设置样式为父页面传递的参数(这增加了新参数borderRadius决定圆角)
在这里插入图片描述
在这里插入图片描述
在父页面表单组件中增加了圆角定义(这里可以不加,因为默认就是5px)
在这里插入图片描述

2、增加上传成功后的图片显示

之前图片上传成功之后有,是会将图片地址暴露给父组件的,这里直接用
在这里插入图片描述


在父页面直接使用该方法
在这里插入图片描述


调用方法设置图片

四、表单中标签的设置

1、官网参考

Element-Tag标签
在这里插入图片描述

2、tag组件初始搭建

(1)新建标签组件

组件下新建Tag.vue
在这里插入图片描述

(2)视图层

直接复制官网提供的代码,在原来基础上增加每个标签的右边距
在这里插入图片描述

(3)逻辑层基本功能实现

根据官网提供的方法完成基本逻辑层代码编写在这里插入图片描述

3、标签使用+标签数据传递

(1)表单中使用标签组件

引入标签组件
在这里插入图片描述


在表单中定义标签类型,并将标签的值传递给标签组件ruleForm[item.field]
在这里插入图片描述

(2)主页面中修改标签的类型

将标签的类型修改为tag
在这里插入图片描述

(3)标签组件获取传递的标签数组

在Tag组件中,获取传递的tagArray的值
在这里插入图片描述


使用watch监听,将获取的值放入标签数组dynamicTags

  • 监听参数中的tagArray,如果有变化,就将标签数组dynamicTags的值设置为解析过的tagArray的最新值
  • 这里使用数组解析[…newVal]是为了不影响别的地方设置的标签
    在这里插入图片描述

4、解决提交表单标签的数据问题

发现以上代码完成后,表单中提交后,标签的值未进行变化,这里需要每次标签值变化时进行监听,并将值返回给父页面,然后再在父页面获取值,并将该项的值设置为获取到的值


标签页在每个需要数据变化的时候执行emit将数据返回给父页面
在这里插入图片描述


父页面处理数据
父页面中调用方法,获取到变化后的数据,并将值直接赋值给该标签
在这里插入图片描述

五 、富文本编辑器

1、vue-markdown的安装

官网:v-md-editor
在这里插入图片描述
这里使用npm安装

npm i @kangc/v-md-editor@next -S

安装成功
在这里插入图片描述

2、markdown组件封装

新建组件Markdown.vue
根据官方提供的引入使用语法进行编写
在这里插入图片描述
在这里插入图片描述

3、主页面引入

(1)修改内容类型为markdown

在这里插入图片描述

(2)表单中增加markdown类型

引用markdown组件
在这里插入图片描述


使用富文本
在这里插入图片描述

4、默认值写入

编辑窗口中是有默认值的,一打开表单是希望有默认值的展示
在这里插入图片描述

5、监听markdown中数据变化

当markdown中数据变化,需要实时更新父组件中的对应的值,以便提交的时候能准确获取到markdown的最新数据
markdown实时监听其中的数据变化,并将值暴露给父页面
在这里插入图片描述


父页面中执行方法,并将更新的数据赋值给当前项
在这里插入图片描述

6、图片上传

(1)建立多文件上传的api

新建接口:传递必要的数组参数files
在这里插入图片描述


新建期望:成功后返回多张图片信息
在这里插入图片描述

(2)参考官网

图片上传的基本用法
在这里插入图片描述


图片上传的基本参数
在这里插入图片描述

(3)加入图片上传功能

增加文件上传的必要参数

  • 上传图片菜单默认为禁用状态 设置 disabled-menus 为空数组可以开启。
  • @upload-image :图片上传的事件
  • upload-image-config:图片上传的基本属性
    • accept:允许上传图片类型
    • multiple:是否允许上传多张图片
      在这里插入图片描述

方法实现
根据官方给的方法进行编写

  • 执行上传
  • 接口请求
  • 返回的数据写入到markdown
    在这里插入图片描述

7、增加代码行号显示

参考官网可知,直接引入插件使用即可
在这里插入图片描述
在这里插入图片描述

六 、解决单条数据查询数据量大的问题

由于内容这个字段一般比较多,所以全部请求可能或导致数据返回不完整,所以需要用到单条数据请求返回数据进行查询
之前权限管理使用过类似功能,只需要传递apiUrl的信息即可

1、参考

传递参数与url的值
在这里插入图片描述


Table组件中会接收信息,并根据路径进行请求,最后将值传递给表单并赋值,进行显示
在这里插入图片描述

2、Apifox接口信息

在之前文章中已经完成了接口书写
可参考:vue3:十八、内容管理-实现内容的数据展示,开关switch设行,tag标签展示
在这里插入图片描述

3、写入apiUrl

在这里插入图片描述

4、效果展示

可看出已经实现了单项数据请求的功能
在这里插入图片描述

七、完整代码

1、内容管理

src/views/ContentView.vue

<template>
    <Table :columns="columns" :apiUrl='apiUrl' :showPage="true" :showSearch="true" @update:tableData="updateTableData"
        :formItems="formItems">
        <template #tag="{ row }">
            <el-tag v-for="item, index in row.tag" :key="index">
                {
  
  { item }}
            </el-tag>
        </template>
        <template #audit="{ row }">
            <el-tag :type="row.audit == 1 ? 'success' : row.audit == 2 ? 'danger' : 'primary'">
                {
  
  { auditObj[row.audit] }}
            </el-tag>
            <!-- 如果是待签核,可以执行签核 -->
            <div v-if="row.audit == 0" class="mt-10">
                <el-button size="small" type="success" @click="UpdateAuditStatus(row, 1)">同意</el-button>
                <el-button size="small" class="ml-0" type="danger" @click="UpdateAuditStatus(row, 2)">拒绝</el-button>
            </div>
        </template>
        <template #status="{ row }">
            <el-switch v-model="row.status" :loading="row.loading || false"
                :before-change="beforeChange.bind(this, row)" :active-value="1" :inactive-value="0"
                :active-text="statusObj[1]" :inactive-text="statusObj[0]" inline-prompt />
        </template>
        <template #coverimage="{ row, index }">
            <el-image style="width: 100px; height: 100px" :src="row.coverimage" :zoom-rate="1.2" :max-scale="7"
                :min-scale="0.2" :preview-src-list="srcList" show-progress :initial-index="initialindex"
                :preview-teleported="true" :hide-on-click-modal="true" fit="contain" @show="showImg(index)" />
        </template>
    </Table>
</template>
<script setup>
import {
     
      ref, reactive, computed } from 'vue'
import Table from '@/components/Table.vue'
import {
     
      showStatus, auditStatus } from '@/api/status'
import {
     
      ElMessage, ElMessageBox } from 'element-plus'
import {
     
      updateStatus, updateAudit } from '@/api/content'
import {
     
      getCategoryList } from '@/api/category'

//表格列
const columns = [
    {
     
      field: 'id', label: 'ID' },
    {
     
      field: 'title', label: '标题' },
    {
     
      field: 'subtitle', label: '副标题' },
    {
     
      field: 'coverimage', label: '封面图' },
    {
     
      field: 'categroy', label: '分类', searchFormType: 'select', searchList: () => {
     
      return categoryData.value } },
    {
     
      field: 'tag', label: '标签' },
    {
     
      field: 'author', label: '作者' },
    {
     
      field: 'clicknum', label: '点击量', searchFormType: 'numrange' },
    {
     
      field: 'desc', label: '描述' },
    {
     
      field: 'status', label: '状态', searchFormType: 'radio', searchList: () => {
     
      return statusArray.value } },
    {
     
      field: 'audit', label: '审核状态', searchFormType: 'select', searchList: () => {
     
      return auditArray.value } },
    {
     
      field: 'createtime', label: '创建时间', searchFormType: 'daterange' },
]
//路径
const apiUrl = {
     
     
    list: '/content/list',
    del: '/content/del',
    add: '/content/add',
    edit: '/content/edit',
    defaultselect: '/content/getcontentbyid'
}

//状态切换
const beforeChange = (row) => {
     
     
    row.loading = true //开启loading
    return new Promise((resolve) => {
     
     
        //修改后端状态
        updateStatus({
     
      id: row.id }).then(res => {
     
     
            row.loading = false //停止loading
            if (res.code == 1) {
     
     
                ElMessage.success(res.msg || '修改成功')
                return resolve(true)
            }
            else {
     
     
                ElMessage.error(res.msg || '修改失败')
                return resolve(false)
            }
        }).catch(() => {
     
     
            row.loading = false //停止loading
            ElMessage.error('修改失败')
            return resolve(false)
        })
    })
}

//预览图片列表
const srcList = ref([])
const updateTableData = (data) => {
     
     
    srcList.value = data.map(item => item.coverimage)
}

//预览图片索引
const initialindex = ref(0)
const showImg = (index) => {
     
     
    initialindex.value = index
}

//审核状态更改
const UpdateAuditStatus = (row, status) => {
     
     
    //同意
    if (status == 1) {
     
     
        ElMessageBox.confirm('确定审核通过吗?', '提示', {
     
     
            confirmButtonText: '确定',
            cancelButtonText: '取消'
        }).then(() => {
     
     
            updateAudit({
     
      id: row.id, audit: status }).then(res => {
     
     
                if (res.code == 1) {
     
     
                    row.audit = 1; //更改当前行审核状态
                    ElMessage.success(res.msg || '审核成功') //提示信息
                }
                else {
     
     
                    ElMessage.error(res.msg || '审核失败')
                }
            })
        })
    }
    //拒绝
    else {
     
     
        ElMessageBox.prompt('请输入拒绝理由', '拒绝原因', {
     
     
            comfirmButtonText: '提交',
            cancelButton: '取消',
            inputType: 'textarea',
            inputValidator: (val) => {
     
     
                if (!val) return '请输入拒绝理由'
            }
        }).then(({
      
       value }) => {
     
     
            updateAudit({
     
      id: row.id, audit: status, reason: value }).then(res => {
     
     
                if (res.code == 1) {
     
     
                    row.audit = 2; //更改当前行审核状态
                    ElMessage.success(res.msg || '审核成功') //提示信息
                }
                else {
     
     
                    ElMessage.error(res.msg || '审核失败')
                }
            })
        })
    }
}
//定义分类下拉数据
const categoryData = ref({
     
     })

//获取分类
getCategoryList().then(res => {
     
     
    if (res.code == 1) {
     
     
        categoryData.value = res.data.map(item => ({
     
     
            value: item.id,
            label: item.name
        }));
    }
})

// 显示状态
const statusObj = ref({
     
     })
const statusArray = ref([])
showStatus().then(res => {
     
     
    if (res.code == 1) {
     
     
        res.data.forEach((item) => {
     
     
            statusObj.value[item.value] = item.label
        })
        statusArray.value = res.data
    }
})

// 审核状态
const auditObj = ref({
     
     })
const auditArray = ref([])
auditStatus().then
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

25号底片~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值