1.参考
使用SpringBoot优雅的实现文件上传_51CTO博客_springboot 上传文件
2.postman测试
报错 :postman调用时body参数中没有file单词
Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
1)headers设置
Content-Type multipart/form-data; boundary=<calculated when request is sent>
2)body
选form-data
key中一定要有file单词,再在旁边文件中选择文件即可
3.报错
Files::getMd5
是Java NIO(New Input/Output)包中Files
类的一个静态方法,用于计算文件的MD5哈希值。这个方法在Java 17中引入,因此,要使用这个方法,你需要确保你的Java版本是17或更高。
4后端代码
controller
@PostMapping("/add")
public ResponseResult addGoods(@RequestParam("name")
String name,
@RequestParam("categoryId")
String categoryid,
@RequestParam("file") MultipartFile file
) throws IOException {
//处理文件
//获取文件原始名称
String originalFilename = file.getOriginalFilename();
System.out.println(originalFilename);
//获取文件的类型
String type = FileUtil.extName(originalFilename);
// log.info("文件类型是:" + type);
//获取文件大小
long size = file.getSize();
//文件目录是否存在判断
File uploadParentFile = new File(fileUploadPath);
//判断文件目录是否存在
if(!uploadParentFile.exists()) {
//如果不存在就创建文件夹
uploadParentFile.mkdirs();
}
//定义一个文件唯一标识码(UUID)
String uuid = UUID.randomUUID().toString();
String fileUUID = uuid + StrUtil.DOT + type;
File uploadFile = new File(fileUploadPath + fileUUID);
String url;
//把上出来的文件转移到uploadFile中去
file.transferTo(uploadFile);
url = "http://localhost:8080/files/" + fileUUID;
// File uploadFile = new File(fileUploadPath + uuid + StrUtil.DOT + type);
Goods goods=new Goods();
goods.setName(name);
goods.setCategoryId(Integer.parseInt(categoryid));
goods.setFileurl(url);
iGoodsService.save(goods);
return ResponseResult.success(goods);
}
service
package com.tencent.wxcloudrun.goods.service;
import com.tencent.wxcloudrun.goods.entity.Goods;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author songhui
* @since 2024-12-14
*/
public interface IGoodsService extends IService<Goods> {
}
serviceimpl
package com.tencent.wxcloudrun.goods.service.impl;
import com.tencent.wxcloudrun.goods.entity.Goods;
import com.tencent.wxcloudrun.goods.mapper.GoodsMapper;
import com.tencent.wxcloudrun.goods.service.IGoodsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author songhui
* @since 2024-12-14
*/
@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements IGoodsService {
}
mapper
package com.tencent.wxcloudrun.goods.mapper;
import com.tencent.wxcloudrun.goods.entity.Goods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author songhui
* @since 2024-12-14
*/
public interface GoodsMapper extends BaseMapper<Goods> {
}
entity
package com.tencent.wxcloudrun.goods.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* <p>
*
* </p>
*
* @author songhui
* @since 2024-12-14
*/
@TableName("sh_goods")
@ApiModel(value = "Goods对象", description = "")
public class Goods implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("商品id")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty("分类id")
private Integer categoryId;
@ApiModelProperty("SPU id")
private Integer spuId;
@ApiModelProperty("编号")
private String sn;
@ApiModelProperty("名称")
private String name;
@ApiModelProperty("关键词")
private String keyword;
@ApiModelProperty("图片")
private String picture;
@ApiModelProperty("提示")
private String tips;
@ApiModelProperty("描述")
private String description;
@ApiModelProperty("详情")
private String content;
@ApiModelProperty("价格")
private BigDecimal price;
@ApiModelProperty("库存")
private Integer stock;
@ApiModelProperty("评分")
private BigDecimal score;
@ApiModelProperty("是否上架")
private Byte isOnSale;
@ApiModelProperty("是否删除")
private Byte isDel;
@ApiModelProperty("是否包邮")
private Byte isFreeShipping;
@ApiModelProperty("销量计数")
private Integer sellCount;
@ApiModelProperty("评论计数")
private Integer commentCount;
@ApiModelProperty("上架时间")
private Integer onSaleTime;
@ApiModelProperty("创建时间")
private Integer createTime;
@ApiModelProperty("更新时间")
private Integer updateTime;
@ApiModelProperty("作者")
private String author;
@ApiModelProperty("文件地址")
private String fileurl;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public Integer getSpuId() {
return spuId;
}
public void setSpuId(Integer spuId) {
this.spuId = spuId;
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
public String getTips() {
return tips;
}
public void setTips(String tips) {
this.tips = tips;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public BigDecimal getScore() {
return score;
}
public void setScore(BigDecimal score) {
this.score = score;
}
public Byte getIsOnSale() {
return isOnSale;
}
public void setIsOnSale(Byte isOnSale) {
this.isOnSale = isOnSale;
}
public Byte getIsDel() {
return isDel;
}
public void setIsDel(Byte isDel) {
this.isDel = isDel;
}
public Byte getIsFreeShipping() {
return isFreeShipping;
}
public void setIsFreeShipping(Byte isFreeShipping) {
this.isFreeShipping = isFreeShipping;
}
public Integer getSellCount() {
return sellCount;
}
public void setSellCount(Integer sellCount) {
this.sellCount = sellCount;
}
public Integer getCommentCount() {
return commentCount;
}
public void setCommentCount(Integer commentCount) {
this.commentCount = commentCount;
}
public Integer getOnSaleTime() {
return onSaleTime;
}
public void setOnSaleTime(Integer onSaleTime) {
this.onSaleTime = onSaleTime;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getFileurl() {
return fileurl;
}
public void setFileurl(String fileurl) {
this.fileurl = fileurl;
}
@Override
public String toString() {
return "Goods{" +
"id = " + id +
", categoryId = " + categoryId +
", spuId = " + spuId +
", sn = " + sn +
", name = " + name +
", keyword = " + keyword +
", picture = " + picture +
", tips = " + tips +
", description = " + description +
", content = " + content +
", price = " + price +
", stock = " + stock +
", score = " + score +
", isOnSale = " + isOnSale +
", isDel = " + isDel +
", isFreeShipping = " + isFreeShipping +
", sellCount = " + sellCount +
", commentCount = " + commentCount +
", onSaleTime = " + onSaleTime +
", createTime = " + createTime +
", updateTime = " + updateTime +
", author = " + author +
", fileurl = " + fileurl +
"}";
}
}
数据表
DROP TABLE IF EXISTS `sh_goods`;
CREATE TABLE `sh_goods` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '商品id',
`category_id` int UNSIGNED NULL DEFAULT 0 COMMENT '分类id',
`spu_id` int UNSIGNED NULL DEFAULT 0 COMMENT 'SPU id',
`sn` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '' COMMENT '编号',
`name` varchar(120) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '' COMMENT '名称',
`keyword` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '' COMMENT '关键词',
`picture` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '' COMMENT '图片',
`tips` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '' COMMENT '提示',
`description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '' COMMENT '描述',
`content` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL COMMENT '详情',
`price` decimal(10, 2) UNSIGNED NULL DEFAULT 0.00 COMMENT '价格',
`stock` int UNSIGNED NULL DEFAULT 0 COMMENT '库存',
`score` decimal(3, 2) UNSIGNED NULL DEFAULT 0.00 COMMENT '评分',
`is_on_sale` tinyint UNSIGNED NULL DEFAULT 0 COMMENT '是否上架',
`is_del` tinyint UNSIGNED NULL DEFAULT 0 COMMENT '是否删除',
`is_free_shipping` tinyint UNSIGNED NULL DEFAULT 0 COMMENT '是否包邮',
`sell_count` int UNSIGNED NULL DEFAULT 0 COMMENT '销量计数',
`comment_count` int UNSIGNED NULL DEFAULT 0 COMMENT '评论计数',
`on_sale_time` int UNSIGNED NULL DEFAULT 0 COMMENT '上架时间',
`create_time` int UNSIGNED NULL DEFAULT 0 COMMENT '创建时间',
`update_time` int UNSIGNED NULL DEFAULT 0 COMMENT '更新时间',
`author` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '作者',
`fileurl` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '文件地址',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
pom.xml
<java.version>1.8</java.version>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tencent</groupId>
<artifactId>springboot-wxcloudrun</artifactId>
<version>1.0</version>
<name>springboot-wxcloudrun</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-bom</artifactId>
<version>3.5.9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.33</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.3.5</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.14</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.33</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
</project>
自动生成代码
package com.tencent;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.sql.Types;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
//修改位置1
FastAutoGenerator.create("jdbc:mysql:///lvbo", "root", "123456")
.globalConfig(builder -> {
//修改位置2
builder.author("songhui") // 设置作者
//修改位置3 关闭swagger
.enableSwagger()
// .fileOverride() // 覆盖已生成文件
//修改位置4
.outputDir("D:\\git\\lvbo2\\src\\main\\java"); // 指定输出目录绝对路径,跟启动类一样的路径
})
.dataSourceConfig(builder ->
builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
})
)
.packageConfig(builder ->
//修改位置5 必须放到主入口同一级别下,不然各种报错
builder.parent("com.tencent.wxcloudrun")
// 设置父包名//****1修改位置6----------------------每次新表修改
.moduleName("goods") // 设置父包模块名
// ****2修改位置7 指定xml文件存放位置
.pathInfo(Collections.singletonMap(OutputFile.xml, "D:\\git\\lvbo2\\src\\main\\resources\\mapper\\goods")) // 设置mapperXml生成路径
)
.strategyConfig(builder ->
//修改位置8 意思是扫描这张表,并且生成实体类,如果不过滤TablePrefix,名字就会带前缀
//*****3 builder.addInclude("lvbo_user","lvbo_category") // 设置需要生成的表名
builder.addInclude("sh_goods") // 设置需要生成的表名
.addTablePrefix("sh_") // 设置过滤表前缀
)
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
// 表1 用户表(sh_user)
// 表2 商品分类表(sh_goods_category)
// 表3 商品表(sh_goods)
// 表4 商品spu表(sh_goods_spu)
// 表5 商品规格表(sh_goods_spec)
// 表6 商品规格项表(sh_goods_spec_item)
// 表7 商品规格项组合表(sh_goods_spec_set)
// 表8 商品属性表(sh_goods_attr)
// 表9 商品属性值表(sh_goods_attr_value)
// 表10 商品筛选表(sh_goods_selector)
// 表11 商品筛选值表(sh_goods_selector_value)
文件存储位置
D:\\git\\lvbo2\\src\\main\\java
为了试试能不能通过浏览器访问,所以放在这里了
生成地址举例
http://localhost:8080/files/0de91001-7cf5-4077-9ad2-db6abaf81d03.txt
5前端代码
有空精简
环境vue3 ts antdv
D:\code\lvbovue3frontpc3\src\request
axios.ts
import axios from 'axios'
import { showMessage } from "./status"; // 引入状态码文件
// https://zhuanlan.zhihu.com/p/398499423
// 创建axios实例
const request = axios.create({
baseURL: 'http://localhost:8080',// 所有的请求地址前缀部分
timeout: 80000, // 请求超时时间(毫秒)
withCredentials: true,// 异步请求携带cookie
// headers: {
// 设置后端需要的传参类型
// 'Content-Type': 'application/json',
// 'token': x-auth-token',//一开始就要token
// 'X-Requested-With': 'XMLHttpRequest',
// },
})
// request拦截器https://zhuanlan.zhihu.com/p/398499423
request.interceptors.request.use(
config => {
// 如果你要去localStor获取token,(如果你有)
// let token = localStorage.getItem("x-auth-token");
// if (token) {
//添加请求头
//config.headers["Authorization"]="Bearer "+ token
// }
return config
},
error => {
// 对请求错误做些什么
Promise.reject(error)
}
)
// response 拦截器
request.interceptors.response.use(
response => {
// 对响应数据做点什么
return response.data
},
error => {
// 对响应错误做点什么
return Promise.reject(error)
}
)
export default request
api.ts
import { ref } from "vue";
import instance from "./axios";
import axios from "axios";
//一般情况下,接口类型会放到一个文件
// 下面两个TS接口,表示要传的参数
interface ReqLogin {
name: string
paw: string
}
interface ReqStatus {
id: string
navStatus: string
}
// Res是返回的参数,T是泛型,需要自己定义,返回对数统一管理***
type Res<T> = Promise<ItypeAPI<T>>;
// 一般情况下响应数据返回的这三个参数,
// 但不排除后端返回其它的可能性,
interface ItypeAPI<T> {
data: T,//请求的数据,用泛型
msg: string | null // 返回状态码的信息,如请求成功等
code: number //返回后端自定义的200,404,500这种状态码
}
// post请求 ,没参数
export const LogoutAPI = (): Res<null> =>
instance.post("/admin/logout");
// post请求,有参数,如传用户名和密码
export const loginAPI = (data: ReqLogin): Res<string> =>
instance.post("/admin/login", data);
// post请求 ,没参数,但要路径传参
export const StatusAPI = (data: ReqStatus): Res<null> =>
instance.post(`/productCategory?ids=${data.id}&navStatus=${data.navStatus}`);
// get请求,没参数,
export const FlashSessionListApi = (): Res<null> =>
instance.get("/flashSession/list");
// get请求,有参数,路径也要传参 (也可能直接在这写类型,不过不建议,大点的项目会维护一麻烦)
export const ProductCategoryApi = (params: { parentId: number }): any =>
instance.get(`/productCategory/list/${params.parentId}`, { params });
// get请求,有参数,(如果你不会写类型也可以使用any,不过不建议,因为用了之后 和没写TS一样)
export const AdminListAPI = (params:any): any =>
instance.get("/admin/list", { params });
// ------------------------------------文件目录操作------------------------------------------------
interface Category {
name: string,
parentid:string,
id:string
}
// 查询目录
export const CategoryListApi = (): Res<null> =>
instance.get("/goods/goodsCategory/getAll");
// 添加 post请求 ,参数,但要路径传参
export const AddCategoryAPI = (data: Category): Res<null> =>
instance.post(`/goods/goodsCategory/add?name=${data.name}&parentid=${data.parentid}`);
// 修改 post请求 ,参数,但要路径传参
export const updateCategoryAPI = (data: Category): Res<null> =>
instance.put(`/goods/goodsCategory/update?name=${data.name}&id=${data.id}`);
// 删除文件目录
export const DelCategoryApi = (id:string): Res<null> =>
instance.delete(`/goods/goodsCategory/del?id=${id}`);
// ------------------------------------文件操作------------------------------------------------
interface Goods {
id:string,
category_id:string,
name: string,
author:string,
description:string,
fileurl:string
}
interface insertbooks {
category_id:string,
name: string,
}
// 查询目录
export const GoodsListApi = (): Res<null> =>
instance.get("/goods/goods/getAll");
//使用原始axios处理带参数文件请求
// const baseURL='http://localhost:8080'
// export function uploadFileWithFileds(param: any) {
// return axios.post(baseURL+'/good/good/add',param).then((res)=>{
// return res
// })
// }
// 添加 post请求 ,参数,但要路径传参
// const formData = new FormData();
// export const AddGoodsAPI = (data: insertbooks): Res<null> =>{
export const AddGoodsAPI = (formData:FormData)=>{
instance.post("/goods/goods/add",formData);
}
// 修改 post请求 ,参数,但要路径传参
export const updateGoodsAPI = (data: insertbooks): Res<null> =>
instance.put(`/goods/goods/update?name=${data.name}&&categoryid=${data.category_id}`);
// 删除文件目录
export const DelGoodsApi = (id:string): Res<null> =>
instance.delete(`/goods/goods/del?id=${id}`);
status.ts
export const showMessage = (status:number|string) : string => {
let message:string = "";
switch (status) {
case 400:
message = "请求错误(400)";
break;
case 401:
message = "未授权,请重新登录(401)";
break;
case 403:
message = "拒绝访问(403)";
break;
case 404:
message = "请求出错(404)";
break;
case 408:
message = "请求超时(408)";
break;
case 500:
message = "服务器错误(500)";
break;
case 501:
message = "服务未实现(501)";
break;
case 502:
message = "网络错误(502)";
break;
case 503:
message = "服务不可用(503)";
break;
case 504:
message = "网络超时(504)";
break;
case 505:
message = "HTTP版本不受支持(505)";
break;
default:
message = `连接出错(${status})!`;
}
return `${message},请检查网络或联系管理员!`;
};
//https://cloud.tencent.com/developer/article/1916167
view
主要是用a-form包裹了 a upload
<a-upload
v-model:file-list="fileList"
name="文件上传"
:beforeUpload="beforeUpload"
>
<a-button> 选择文件 </a-button>
</a-upload>
先定一个formData
用于准备所有信息(包括文件)给axios发送
const formData=new FormData();
FormData应该是一种比较好用的js新格式,参考
https://juejin.cn/post/7258171660145803324
获得file
这里的:beforeUpload特别重要,主要是 其获得了上传的file
const beforeUpload=(file)=>{
formData.append('file',file)
}
formdata.append既可以添加属性给它,也可以添加文件
formstate
用于收集非附件的其它form字段信息
因为文件上传框是个弹出框,所以用了a-modal
<a-modal>
<a-form>
<a-form-item>
<a-upload>
给a-form绑定了一个属性formstate
<template>
<div style="margin-left: 16px">
<div style="margin-bottom: 16px">
<a-button type="primary" :disabled="!hasSelected" :loading="state.loading" @click="start">
Reload
</a-button>
<!-- 因为选中的要删除的行的id存在state.selectedRowKeys里面 -->
<a-button type="primary" @click="delBook(state.selectedRowKeys)">
删除书籍
</a-button>
<a-button type="primary" @click="addBook">
新增书籍
</a-button>
<span style="margin-left: 8px">
<template v-if="hasSelected">
{{ `Selected ${state.selectedRowKeys.length} items` }}
</template>
</span>
</div>
<!--必须指定rowKey,可以是当前data中的一个属性 ,不指定的话就会被全部选中-->
<a-table
rowKey="id"
:row-selection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }"
:columns="columns"
:data-source="data"
/>
</div>
<!-- 弹出框 a-modal 默认隐藏,是在ContextMenuClick让其显示出来-->
<a-modal
v-model:open="open"
:title="modalTitle"
@ok="handleOk"
@cancel="resetForm"
okText="确认"
cancelText="取消"
:maskClosable="false"
:closable="false"
>
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item ref="categoryId" label="分类编号" name="categoryId">
<a-input
v-model:value="formState.categoryId"
placeholder="分类编号"
/>
</a-form-item>
<a-form-item ref="name" label="书名" name="name">
<a-input
v-model:value="formState.name"
placeholder="书名"
/>
</a-form-item>
<a-form-item ref="author" label="作者" name="author">
<a-input
v-model:value="formState.author"
placeholder="作者"
/>
</a-form-item>
<a-form-item ref="description" label="简介" name="description">
<a-input
v-model:value="formState.description"
placeholder="简介"
/>
</a-form-item>
<a-form-item name="fileList" label="上传文件">
<!-- <a-upload
v-model:file-list="fileList"
name="文件上传"
action=""
:customRequest="upDown"
:beforeUpload="beforeUpload"
@remove="removeFile"
accept=".xlsx,.xls"
> -->
<a-upload
v-model:file-list="fileList"
name="文件上传"
:beforeUpload="beforeUpload"
>
<a-button> 选择文件 </a-button>
</a-upload>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { message, Modal, Upload} from 'ant-design-vue';
import { computed, onMounted, reactive, ref, toRaw } from 'vue';
import { AddGoodsAPI, DelGoodsApi, GoodsListApi, updateGoodsAPI, uploadFileWithFileds } from '../../request/api';
const props = defineProps({
parentData: String
});
type Key = string | number;
interface DataType {
key: Key;
categoryId:string,
name: string;
author: string;
description: string;
fileurl:string;
}
const columns = [
{
title: '分类',
dataIndex: 'categoryId',
},
{
title: '书名',
dataIndex: 'name',
},
{
title: '作者',
dataIndex: 'author',
},
{
title: '简介',
dataIndex: 'description',
},
];
let data=ref<DataType[]>([]);
// for (let i = 0; i < 46; i++) {
// data.push({
// key: i,
// name: `Edward King ${i}`,
// age: 32,
// address: `London, Park Lane no. ${i}`,
// });
// }
//open代替了visible
const open = ref(false);
const modalTitle = ref("新增书籍");
const formRef = ref();
// 重要的formState
console.log("props.parentData",props.parentData)
//文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件文件
//存储文件
const fileList = ref<any[]>([]);
// 自定义上传,可以自定义上传接口,不用通过action属性,还是非常灵活的
const customRequestMethod=(file:File)=> {
// const { onSuccess, onError, file, onProgress } = options;
const formData=new FormData();
}
//
const formData=new FormData();
const formState = reactive({
name:'',
categoryId: '',
description:'',
author:'',
});
const beforeUpload=(file)=>{
formData.append('file',file)
}
//https://blog.csdn.net/dashinihao/article/details/118630567
// const fileList = ref<UploadProps['fileList']>()
interface FileInfo {
uid: string;
name: string;
status?: string;
// 其他可能的属性...
}
// const fileList=[]
const rules = {
name: [
{
required: false,
message: "请输入父节点编号",
trigger: "blur",
},
],
};
const resetForm = () => {
// formRef.value.resetFields();
};
const handleOk = () => {
if (modalTitle.value == "新增书籍") {
// const formData = ref(new FormData());
formData.append('name',formState.name);
formData.append('categoryId',formState.categoryId);
console.log("name----",formData.get('name'))
console.log("hell0----",formData.get('file'))
//新增文件
// uploadFileWithFileds(formData)
AddGoodsAPI(formData)
}
}
// const handleOk = (e) => {
// console.log(e);
// // console.log("hello");
// formRef.value
// .validate()
// .then(() => {
// console.log("values", formState, toRaw(formState), e);
// if (modalTitle.value == "新增书籍") {
// //新增文件
// AddGoodsAPI({
// name: formState.name,
// category_id: formState.categoryId
// })
// .then((res) => {
// if (res.data.status == 0) {
// message.error("请修改");
// } else {
// message.success("上传成功")
// }
// })
// .catch(() => {
// message.error("新增失败请联系管理员");
// })
// .finally(() => {
// resetForm();
// open.value = false;
// });
// } else {
// //修改文件
// updateGoodsAPI(
// {
// name: formState.name,
// category_id:''
// }
// )
// .then((res) => {
// if (res.data.status == 200) {
// message.success("修改成功");
// }
// else {
// message.error("修改失败请联系管理员");
// }
// })
// .catch(() => {
// message.error("修改失败请联系管理员");
// })
// .finally(() => {
// resetForm();
// open.value = false;
// });
// }
// })
// .catch((error) => {
// console.log("error", error);
// });
// };
onMounted(
()=>getData()
)
const getData=async () => {
await GoodsListApi().then(
response=>{
// console.log("res",response.data)
// x.value=response.data;
// data.value=response.data;
data.value=response.data;
console.log(data)
}
);
}
const state = reactive<{
selectedRowKeys: Key[];
loading: boolean;
}>({
selectedRowKeys: [], // Check here to configure the default column
loading: false,
});
const hasSelected = computed(() => state.selectedRowKeys.length > 0);
const start = () => {
state.loading = true;
// ajax request after empty completing
setTimeout(() => {
state.loading = false;
state.selectedRowKeys = [];
}, 1000);
};
const onSelectChange = (selectedRowKeys: Key[]) => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
state.selectedRowKeys = selectedRowKeys;
console.log("当前选择",state.selectedRowKeys)
};
//删除
const delBook=(key:any)=>{
Modal.confirm({
title: '确认删除此数据?',
// icon: createVNode(IconComponent),
onOk() {
DelGoodsApi(key).then(res => {
if (res.data.code === 0) {
message.success("删除成功")
// subareaList()
} else {
message.error(res.data.msg)
}
})
},
onCancel() { },
}
)
}
//添加
const addBook=()=>{
open.value=true
}
</script>
发给axios
由a-modal上的handleOk来触发调用后端
<a-modal
v-model:open="open"
:title="modalTitle"
@ok="handleOk"
@cancel="resetForm"
okText="确认"
cancelText="取消"
:maskClosable="false"
:closable="false"
>
AddGoodsAPI(formData)