使用 Ant-Design-Vue 制作一个带图片上传功能的表单对话框
功能需求
使用 Antdv 的 Modal 组件内嵌一个 a-form 表单,具有添加数据和图片的功能。




页面结构设计
<template>
<!--Modal-->
<a-modal>
<div>
<a-form>
<a-form-item label="水果名称" name="fruitName">
<a-input type="text" placeholder="水果名称">
</a-form-item>
<a-form-item label="价 格" name="price">
<a-input type="number" placeholder="价格(元/公斤)">
</a-form-item>
<a-form-item label="库 存" name="stock">
<a-input type="number" placeholder="库存(公斤)">
</a-form-item>
<a-form-item label="图 片">
<a-upload>
<div>
<img v-if="!base64img" src="@/assets/plus.png">
<!-- ↑ 如果没有添加图片则显示一个加号占位符图片 -->
<img v-else :src="base64img">
<!-- ↑ 如果添加了图片则显示图片 -->
</div>
</a-upload>
</a-form-item>
<a-form-item>
<a-button>提交</a-button>
</a-form-item>
</a-form>
</div>
</a-modal>
</template>
只展示了基础的页面结构,省略了绑定的事件、逻辑和样式代码。
思路整理
Modal 中有一个表单,表单中有 4 个子项目,前三个为 input 输入框,最后一个是图片上传的组件,均为必填项。所以要为前三个输入框加上前端的表单验证,只需要在 a-form 的 :rules 中加入 [{required: true, message: ...}]。上传图片则采用单独的逻辑判断,因为表单输入的前三项和图片上传采用的是不同的接口,上传表单数据(水果名称、价格、库存)和图片的路径使用的是一个接口,上传图片至服务器使用的是另一个接口。所以我们要在点击提交按钮时添加一个判断图片是否为空的操作,如果为空则拒绝向服务器发送请求。
关于图片上传的部分可以看 这篇文章
由于 Antdv 的 Modal 组件是有默认的 确认 和 取消 按钮,这点对于提交表来来说不太方便,所以要禁用默认的按钮,再向表单中添加一个 form-item 提交按钮。
在上传文件时,为了避免上传相同名称的文件,我们要用当前时间戳来为文件重命名,手段是 const renameFile = new File(...)
添加数据成功后,需要重新从数据库中获取下数据,这个方法直接使用 onMounted 中的就可以了。由于数据量较少,故没有在服务端使用分页,每次请求都会返回数据库中的所有数据。
逻辑部分
// ↓ 控制 Modal 组件是否显示
const addModalVisible = ref<boolean>(false);
// 点击显示 Modal 的回调
function showAddModal() {
console.log("Show Add Modal");
addModalVisible.value = true;
}
// 表单数据
const formData = reactive({
fruitName: "",
price: "",
stock: "",
avatar: "",
})
/**
* 上传图片需要用到的变量
*/
const fileList = ref();
// ↑ 放置图片的
const base64img = ref();
// ↑ 用来放置 base64 转码后的图片
const btnLoading = ref<boolean>(false);
// 控制 提交 按钮的 loading 状态
/**
* 上传文件的回调
*/
function beforeUpload(file: any) {
const isImg = (file.type === 'image/jpeg' || file.type === 'image/png');
// 如果文件不是图片格式则禁止上传
if (!isImg) {
Notice.error("只能上传 jpeg/jpg/png 格式的文件!");
// ↑ Notice.error 是我自己写的 Notice 类的一个静态函数
// 用的是 antdv 的 Notifiacation
return Upload.LIST_IGNORE;
// ↑ 这个不解释了,上一篇博客中提到了
}
fileList.value = file;
// ↓ 获取所需要上传图片的 Base64 编码
getBase64(fileList.value, (cb_img: string) => {
// 将获取到的所要上传图片的 Base64 编码渲染到图片上
base64img.value = cb_img;
})
return false;
}
/**
* 待上传图片被删除的回调
*/
function handleRemove() {
fileList.value = null;
// ↑ 置空待上传文件列表
base64img.value = null;
// ↑ 显示默认占位图
}
/**
* 点击 Modal 中的添加按钮的回调
*/
function addItem() {
// 添加图片文件到服务器指定位置
const imgForm = new FormData();
if (fileList.value != null) {
// ↓ 以当前时间戳重命名文件
const newFileName = Date.now() + "." + fileList.value.name.substring(fileList.value.name.lastIndexOf(".") + 1);
// ↓ avatar 是数据库中储存图片地址的字段,后面会加上地址重新赋值一次
formData.avatar = newFileName;
const renameFile = new File([fileList.value], newFileName);
// ↑ 创建新文件:以新文件名重命名文件
imgForm.append("file", renameFile);
} else {
Notice.error("图片不能为空!");
return Upload.LIST_IGNORE;
}
btnLoading.value = true;
// 将图片上传至服务器
const addData = new Promise((resolve, reject) => {
// fruitApi 是自己封装的 Axios
fruitApi({
method: 'put',
url: 'api/upload',
data: imgForm,
})
.then((resp) => {
resolve(resp);
})
.catch((error) => {
reject(error);
})
})
// 添加数据至数据库
// 并添加文件的名称和路径至数据库
const uploadPic = new Promise((resolve, reject) => {
fruitApi({
method: 'post',
url: 'api/fruits',
data: {
fruitName: formData.fruitName,
avatar: imagesAddr + "/" + formData.avatar,
// imagesAddr 是服务器储存图片的地址
price: formData.price,
stock: formData.stock,
}
})
.then((resp: any) => {
resolve(resp);
})
.catch((error) => {
reject(error);
})
})
Promise.all([addData, uploadPic])
.then((resp) => {
console.log("请求成功: ");
console.log(resp);
changeUploadItemColor("success");
// ↑ 上传成功改变颜色,也在上个博客提到过了
Notice.success("添加成功!");
setTimeout(() => {
getFruits();
// ↑ 上传成功后重新获取数据
}, 500)
})
.catch((error) => {
console.log("请求失败: ");
console.log(error);
changeUploadItemColor("error");
Notice.error("添加失败~");
})
.finally(() => {
btnLoading.value = false;
// 取消按钮的 loading 状态
})
}
完善页面结构
<!--Modal-->
<a-modal v-model:visible="addModalVisible"
title="模态对话框"
:confirm-loading="btnLoading"
:footer="null"
>
<div>
<a-form
:model="formData"
@finish="addItem"
>
<a-form-item label="水果名称" name="fruitName" :rules="[{required: true, message: '请输入水果名称'}]">
<a-input type="text" placeholder="水果名称" v-model:value="formData.fruitName"/>
</a-form-item>
<a-form-item label="价 格" name="price" :rules="[{required: true, message: '请输入价格'}]">
<a-input type="number" placeholder="价格(元/公斤)" suffix="RMB" v-model:value="formData.price"/>
</a-form-item>
<a-form-item label="库 存" name="stock" :rules="[{required: true, message: '请输入库存'}]">
<a-input type="number" placeholder="库存(公斤)" v-model:value="formData.stock"/>
</a-form-item>
<a-form-item label="图 片">
<a-upload
:before-upload="beforeUpload"
:max-count="1"
@remove="handleRemove"
>
<div>
<img v-if="!base64img" src="@/assets/plus.png" />
<img v-else :src="base64img" />
</div>
</a-upload>
</a-form-item>
<a-form-item>
<a-button html-type="submit" :loading="btnLoading">提交</a-button>
</a-form-item>
</a-form>
</div>
</a-modal>
完整代码:https://blog-static.cnblogs.com/files/blogs/754613/modalForm.vue.js?t=1665405534
完
使用 Ant-Design-Vue 制作一个带图片上传功能的表单对话框的更多相关文章
- 基于Ant Design Vue封装一个表单控件
开源代码 https://github.com/naturefwvue/nf-vue3-ant 有缺点本来是写在最后的,但是博文写的似乎有点太长了,估计大家没时间往下看,于是就把有缺点写在前面了,不喜 ...
- Vue 2.x折腾记 - (17) 基于Ant Design Vue 封装一个配置式的表单组件
前言 写了个类似上篇搜索的封装,但是要考虑的东西更多. 具体业务比展示的代码要复杂,篇幅太长就不引入了. 效果图 2019-04-25 添加了下拉多选的渲染,并搜索默认过滤文本而非值 简化了渲染的子组 ...
- Java中request请求之 - 带文件上传的form表单
常用系统开发中总免不了显示图片,保存一些文件资料等操作. 这些操作的背后,就是程序员最熟悉的 enctype="multipart/form-data"类型的表单. 说起file类 ...
- Retrofit 2.0 轻松实现多文件/图片上传/Json字符串/表单
如果嫌麻烦直接可以用我封装好的库:Novate: https://github.com/Tamicer/Novate 通过对Retrofit2.0的前两篇的基础入门和案例实践,掌握了怎么样使用Retr ...
- 基于ASP.Net +easyUI框架上传图片,实现图片上传,提交表单
<body> <link href="../../Easyui/themes/easyui.css" rel="stylesheet" typ ...
- 使用ant design vue的日历组件,实现一个简单交易日与非交易日的切换
使用ant design vue的日历组件,实现一个简单交易日与非交易日的切换 需求: 日历区分交易日.非交易日 可以切换面板查看整年交易日信息 可以在手动调整交易日.非交易日 演示实例 序--使用软 ...
- Ant Design Vue Pro 项目实战-项目初始化(一)
写在前面 时间真快,转眼又是新的一年.随着前后端技术的不断更新迭代,尤其是前端,在目前前后端分离开发模式这样的一个大环境下,交互性.兼容性等传统的开发模式已经显得有些吃力.之前一直用的是react,随 ...
- Ant Design Vue select下拉列表设置默认值
在项目中需要为Ant Design Vue 的 select 组件设置一个默认值,如下图所示的状态下拉选择框,默认选择全部 代码如下: <a-select v-model="query ...
- Vue3学习(二)之集成Ant Design Vue
一.集成Ant Design Vue npm install ant-design-vue@2.0.0-rc.3 --save 兼容性 Ant Design Vue 2.x 支持所有的现代浏览器. 如 ...
- ios学习-制作一个浏览图片的Demo
一.项目要求:制作一个浏览图片的Demo,要求包含夜间模式,以及改变图片大小,能够显示不同的图片描述 二.开发步骤: 1.在storyboard上添加一个空白的View,然后添加”设置“按钮,添加im ...
随机推荐
- C# 开发技巧 轻松监控方法执行耗时
前言 MethodTimer.Fody 是一个功能强大的库,可以用于测量 .NET 应用程序中的方法的执行时间.允许你在不修改代码的情况下,自动地测量和记录方法的执行时间. 这个工具是基于.NET的 ...
- MFC BCG 一些记录
MFC: UpdateData (TRUE) // 更新值到控件 UpdateData (TRUE) // 更新控件到值DECLAREMESSAGEMAPBEGINMESSAGEMAP(d ...
- 2个月搞定计算机二级C语言——真题(1)解析
1. 前言 大家好,我是梁国庆. 这段时间将持续发布计算机二级 C 语言真题的解析,想要同步练习,需要资源包的朋友可以跳转免费获取--<3个月搞定计算机二级C语言--准备工作>. 现在恐怕 ...
- 18B20的CRC官方讲解
理解和运用MAXIM IBUTTON产品中的循环冗余校验(CRC) 摘要 : 全部1-Wire器件,包括iButton器件,都具有唯一的8字节注册码,储存在只读存储器(ROM)中.该注册码在1-Wir ...
- 《最新出炉》系列初窥篇-Python+Playwright自动化测试-61 - 隐藏元素定位与操作
1.简介 对于前端隐藏元素,一直是自动化定位元素的隐形杀手,让人防不胜防.脚本跑到隐藏元素时位置时报各种各样的错误,可是这种隐藏的下拉菜单又没有办法避免,所以非常头痛,这一篇只为交流隐藏元素自动化定位 ...
- 【RabbitMQ】11 深入部分P4 延迟队列
一.延迟队列: 消息经过交换机分配到队列上之后,在到达指定的时间,才会被消费? 需求: 1.下单之后的30分钟,用户未支付,订单取消,回滚库存 2.新用户注册7天后,发送短信慰问,或者是用户生日发送短 ...
- 【Uni-App】底部栏踩坑
一.Page.json一定要注册这个页面 Uni-App通过page.json找到,解析不到这个页面文件是没用的 二.配置tabBar属性 "tabBar":{ "col ...
- 【Java】 WebService 校验机制
测试环境域名 不可见 正式环境域名 不可见 1.2.安全校验凭证 accessId(授权ID) 测试/正式待定 securityKey(加密密钥) 测试/正式待定 1.3.安全校验机制 1.3.1.在 ...
- 【C3】07 盒子模型
在 CSS 中,所有的元素都被一个个的"盒子(box)"包围着, 理解这些"盒子"的基本原理,是我们使用CSS实现准确布局.处理元素排列的关键. 本文围绕 &q ...
- 【MySQL】Windows-5.7.30 解压版 下载安装
1.Download 下载 mysql官网: https://dev.mysql.com/ 找到download点击进入下载页面: https://dev.mysql.com/downloads/ 找 ...