展示
注:本项目做的时候是前后端分离项目
做功能之前首先要考虑几个问题:
1.用户点击以下图片中的1时,X号时把上传的图片删除
2.用户点击以下图片中的2时,点击空白处时把上传的图片删除
3.用户点击以下图片中的3时,点击提交时新增成功
3.用户点击以下图片中的4时,点击取消时把上传的图片删除
注:file1,file,imageUrl这三个变量解释
问:为什么使用3个变量
答:第一 file1,file分别是2个上传照片缓存属性
问:既然缓存属性为什么还定义了imageUrl
答:这个是从缓存中取得就是防止有客户一直上传就是不新增,这样会让 服务器多出好多垃圾数据,就如上面视频上的一样,就是为了防止随意上传,不确认就不新增就把已上传服务器的图片都删除
步骤:
1.首先需要一个全局close事件 无论点击任何地方都必须close触发事件 close Dialog 关闭的回调,然后将这个事件先定义出来
2.data中3个变量定义,为什么是2个图片变量那,因为我上传两个图
data() {
return {
//对象
brand:{
bigPic: '', //实体类属性 图片
logo: '', //实体类属性 图片
}
}
}
data() {
return {
file1:'', //用于新增或取消删除老图片
file:'',//用于新增或取消删除老图片
btn:null, //判断按钮使用 用于判断当前点击的是什么按钮
}
},
//接上传图片成功回调中的值
computed: {
imageUrl() {
let imageUrl= localStorage.getItem('imageUrl');
return imageUrl ? imageUrl : this.imageUrl;
}
},
3.给btn赋值 用于判断当前点击的是什么按钮
//点击弹框提交时,新增
onSubmit(brandFrom){
this.btn = 1,
}
//点击编辑按钮时
handleEdit(id){
//代表点击修改按钮
this.btn = 2;
}
//取消按钮
cancel(){
this.btn=3;
this.editVisible=false //关闭弹框
}
4.给图片变量赋值:
//回显数据
handleEdit(id){
this.btn = 2,
seleteBrandById(id).then(response => {
this.editVisible = true;
this.brand = response.data.data;
//用于新增或取消删除老图片
this.file1=response.data.data.bigPic;
//用于新增或取消删除老图片
this.file=response.data.data.logo;
}).catch(res => {
this.$message.error("获取品牌信息失败");
})
},
上传图片成功后传值:
定义一个数组
5.开始具体判断
//取消删除已上传的图片
close: function (brandFrom) {
//走新增的方法
if (this.btn == 1) {
//修改的时候的方法
}else if(this.btn == 2){
var images = [];
if( this.file!=this.brand.logo){
images.push(this.brand.logo)
}
if( this.file1!=this.brand.bigPic ){
images.push(this.brand.bigPic)
}
//发送请求
deleteFile({"images":images.toString()}).then(response => {
})
}
//取消时删除已上传的图片的方法
else {
//取消删除已上传的图片
let images=[];
//this.imageUrl是上面接过来的缓存中的
images.push(this.imageUrl)
deleteFile({"images":images.toString()}).then(response => {
//刷新 因为这个值存在缓存中不刷新下次就用不了拉
window.location.reload()
})
}
}
}
6.发起请求:
//点击取消是批量删除已上传的图片
export const deleteFile = images=> {
return request({
url:'http://localhost:8080/upload/deleteFile',
method: 'post',
params: images
});
};
7.在Controller类代码:
package com.ff.controller;
import com.ff.oss.OSSUtil;
import com.ff.result.ResultObj;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/upload")
@CrossOrigin
public class OSSController {
//删除图片upload
@RequestMapping("deleteFile")
public void deleteFile(String images) {
//批量时删除图片
if(images!=null) {
//删除图片
String split[] = images.split(",");
for (int i = 0; i <split.length ; i++) {
OSSUtil.deleteFile(split[i]);
}
}
}
}
7.在工具类代码:
package com.ff.oss;
import com.aliyun.oss.OSSClient;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
public class OSSUtil {
// 阿里云oss上传文件相关属性值
// 阿里云API的外网域名
public static final String ENDPOINT = "oss-cn-beijing.aliyuncs.com";
// 阿里云API的密钥
public static final String ACCESS_KEY_ID = "LTAI4Fzm3c6EmUexCHLvZNcm";
// 阿里云API的密钥Access Key Secret
public static final String ACCESS_KEY_SECRET = "b8OrjTpYWW3sZSlimy4sxr2WJuNX09";
// 阿里云API的bucket名称
public static final String BACKET_NAME = "jjjqqq";
// 阿里云API的文件夹名称
public static final String FOLDER = "photo/";
public static final String URl = "https://jjjqqq.oss-cn-beijing.aliyuncs.com/";
public static void deleteFile(String fileName){
OSSClient ossClient = new OSSClient(ENDPOINT,ACCESS_KEY_ID,ACCESS_KEY_SECRET);
//给fileName重新赋值
String replace = fileName.replace(URl, "");
//删除
ossClient.deleteObject(BACKET_NAME,replace);
ossClient.shutdown();
}
//图片上传
public static String uploadFile(MultipartFile file){
OSSClient ossClient=null;
InputStream is=null;
String filePath =null;
try {
//创建ossclient对象 key secret 创建
ossClient=new OSSClient(ENDPOINT,ACCESS_KEY_ID,ACCESS_KEY_SECRET);
//获取文件名
String fileName = file.getOriginalFilename();
//获取后缀
String suffix = fileName.substring(fileName.lastIndexOf("."));
//获取文件新名称
String newFileName = UUID.randomUUID()+suffix;
is = file.getInputStream();
//创建一个已当前时间为文件的文件夹
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
String folderName = simpleDateFormat.format(new Date());
//BUCKET_NAME 文件 file
ossClient.putObject(BACKET_NAME,folderName+"/"+newFileName,is);
filePath = URl+folderName+"/"+newFileName;
} catch (IOException e) {
e.printStackTrace();
}finally {
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ossClient != null){
ossClient.shutdown();
}
}
return filePath;
}
}
下面是前端具体代码:
<template>
<div>
<div class="crumbs">
<el-breadcrumb separator="/">
<el-breadcrumb-item>
<i class="el-icon-lx-cascades"></i> 品牌列表
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="container">
<!--搜索栏-->
<div class="handle-box">
<el-button type="primary" icon="el-icon-plue" class="handle-del mr10" @click="openAdd">添加</el-button>
<el-button type="danger" icon="el-icon-delete" class="handle-del mr10" @click="delAllSelection">批量删除</el-button>
<el-input v-model="query.name" placeholder="用户名" class="handle-input mr10"></el-input>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
</div>
<!--列表栏-->
<el-table
:data="tableData"
border
class="table"
ref="multipleTable"
header-cell-class-name="table-header"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="id" label="编号" width="55" align="center">
<template slot-scope="scope">
{{scope.$index+1}}
</template>
</el-table-column>
<el-table-column prop="name" label="品牌名"></el-table-column>
<el-table-column prop="firstLetter" label="首字母"></el-table-column>
<el-table-column prop="sort" label="排序"></el-table-column>
<el-table-column label="品牌logo" align="center">
<template slot-scope="scope">
<el-image
class="table-td-logo"
:src="scope.row.logo"
:preview-src-list="[scope.row.logo]"
></el-image>
</template>
</el-table-column>
<el-table-column label="品牌制造商" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleFactoryStatusChange(scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.factoryStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleShowStatusChange(scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.showStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column prop="productCount" label="产品数量"></el-table-column>
<el-table-column prop="productCommentCount" label="产品评论数量"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="text" icon="el-icon-edit" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button type="text" icon="el-icon-delete" class="red" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!--分页栏-->
<div class="pagination">
<el-pagination
background
layout="total, prev, pager, next"
:current-page="query.pageNum"
:page-size="query.pageSize"
:total="total"
@current-change="handlePageChange"
></el-pagination>
</div>
</div>
<!-- 编辑弹出框 -->
<el-dialog title="编辑" :visible.sync="editVisible" v-dialogDrag @close="close('brandFrom')" width="40%">
<el-form :model="brand" ref="brandFrom" label-width="150px">
<el-form-item label="品牌名称:" prop="name">
<el-input v-model="brand.name"></el-input>
</el-form-item>
<el-form-item label="品牌首字母:" prop="firstLetter">
<el-input v-model="brand.firstLetter"></el-input>
</el-form-item>
<el-form-item label="品牌LOGO:" prop="logo">
<single-upload v-model="brand.logo"></single-upload>
</el-form-item>
<el-form-item label="品牌专区大图:" prop="bigPic">
<single-upload v-model="brand.bigPic"></single-upload>
</el-form-item>
<el-form-item label="排序:" prop="sort" >
<el-input v-model.number="brand.sort" type="number"></el-input>
</el-form-item>
<el-form-item label="是否显示:" prop="showStatus">
<el-radio-group v-model="brand.showStatus">
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="品牌制造商:" prop="factoryStatus">
<el-radio-group v-model="brand.factoryStatus">
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('brandFrom')">提交</el-button>
<el-button @click="cancel">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import {queryBrandList,updateFactoryStatus,updateShowStatus,saveOrUpdate,deleteBrand,deleteBath,seleteBrandById,deleteFile} from "../../../api/pms/brand/brand";
//引入图片插件
import SingleUpload from '../../../components/upload/singleUpload'
export default {
name: 'brand',
//加载图片插件
components:{SingleUpload},
data() {
return {
brand:{
id:null,
bigPic: '',
brandStory: '',
factoryStatus: 0,
firstLetter: '',
logo: '',
name: '',
showStatus: 0,
sort: 0,
img:"", //用于修改时删除老图片
img1:""//用于修改时删除老图片
},
query: {
address: '',
name: '',
pageNum: 1,
pageSize: 5,
},
tableData: [],
multipleSelection: [],
file1:[], //用于取消删除老图片
file:[],//用于取消删除老图片
title:'',
delList: [],
editVisible: false,
total: 0,
form: {},
btn:null, //判断按钮使用
idx: -1,
id: -1
};
},
created() {
this.getData();
},
computed: {
imageUrl() {
let imageUrl= localStorage.getItem('imageUrl');
return imageUrl ? imageUrl : this.imageUrl;
}
},
methods: {
// 获取 easy-mock 的模拟数据
getData() {
queryBrandList(this.query).then(res => {
this.tableData = res.data.data.list;
this.total = res.data.data.total;
});
},
// 触发搜索按钮
handleSearch() {
this.$set(this.query, 'pageNum', 1);
this.getData();
},
// 删除操作
handleDelete(row) {
// 二次确认删除
this.$confirm('确定要删除吗?', '提示', {
type: 'warning'
}).then(() => {
deleteBrand(row).then(response=>{
this.$message.success(response.data.msg);
// 删除最后一页数据跳转回上一页
const totalPage = Math.ceil((this.total - 1) / this.query.pageSize) // 剩余数据总页数
this.query.pageNum = this.query.pageNum > totalPage ? totalPage : this.query.pageNum
this.query.pageNum = this.query.pageNum < 1 ? 1 : this.query.pageNum
this.getData();
})
}).catch(() => {
this.$message.error(response.data.msg);
});
},
// 多选操作
handleSelectionChange(val) {
this.multipleSelection = val;
},
//批量删除
delAllSelection() {
const length = this.multipleSelection.length;
if(length>0){
// 二次确认删除
this.$confirm('您确定都要删除吗?', '提示', {
type: 'warning'
}).then(() => {
var ids=[];
for (let i = 0; i <this.multipleSelection.length ; i++) {
ids.push(this.multipleSelection[i].id);
}
deleteBath({"ids":ids.toString()}).then(response=>{
this.$message.success(response.data.msg);
// 批量删除最后一页数据跳转回上一页
const totalPage = Math.ceil((this.total - this.multipleSelection.length) / this.query.pageSize) // 剩余数据总页数
this.query.pageNum = this.query.pageNum > totalPage ? totalPage : this.query.pageNum
this.query.pageNum = this.query.pageNum < 1 ? 1 : this.query.pageNum
//查询
this.getData();
})
}).catch(() => {
this.$message.error("请选择要删除的数据");
});
}
},
// 分页导航
handlePageChange(val) {
this.$set(this.query, 'pageNum', val);
this.getData();
},
修改是否品牌供应商
handleFactoryStatusChange(row) {
var data ={
"id":row.id,
"factoryStatus":row.factoryStatus
}
updateFactoryStatus(data).then(response=>{
this.$message.success(response.data.msg);
}).catch(res=>{
this.$message.error("修改品牌供应商失败");
this.getData();
})
},
//修改是否显示
handleShowStatusChange(row) {
var data ={
"id":row.id,
"showStatus":row.showStatus
}
updateShowStatus(data).then(response=>{
this.$message.success(response.data.msg);
}).catch(res=>{
this.$message.error("修改品牌失败");
this.getData();
})
},
// 打开添加页面
openAdd(){
this.editVisible = true
//清空表单数据
for (var key in this.brand){
this.brand[key] = null
}
},
//添加品牌
onSubmit(brandFrom){
this.btn = 1,
saveOrUpdate(this.brand).then(response => {
this.$message.success(this.brand.id==null?"添加成功":"修改成功");
this.getData();
this.editVisible = false;
}).catch(res => {
this.$message.error(this.brand.id==null?"添加失败":"修改失败");
})
},
//回显
handleEdit(id){
this.btn = 2,
seleteBrandById(id).then(response => {
this.editVisible = true;
this.brand = response.data.data;
//赋值用于修改时删除老图片
this.brand.img=response.data.data.bigPic;
//赋值用于修改时删除老图片
this.brand.img1=response.data.data.logo;
//用于取消删除老图片
this.file1=response.data.data.bigPic;
//用于取消删除老图片
this.file=response.data.data.logo;
}).catch(res => {
this.$message.error("获取品牌信息失败");
})
},
//取消按钮
cancel(){
this.btn=3;
this.editVisible=false
},
//取消删除已上传的图片
close: function (brandFrom) {
//走新增的方法
if (this.btn == 1) {
//修改的方法
}else if(this.btn == 2){
var images = [];
if( this.file!=this.brand.logo){
images.push(this.brand.logo)
}
if( this.file1!=this.brand.bigPic ){
images.push(this.brand.bigPic)
}
//发送请求
deleteFile({"images":images.toString()}).then(response => {
})
}
//取消删除已上传的图片的方法
else {
//取消删除已上传的图片
let images=[];
images.push(this.imageUrl)
deleteFile({"images":images.toString()}).then(response => {
//刷新 因为这个值存在缓存中不刷新下次就用不了拉
window.location.reload()
})
}
},
}
};
</script>
<style scoped>
.handle-box {
margin-bottom: 20px;
}
.handle-select {
width: 120px;
}
.handle-input {
width: 300px;
display: inline-block;
}
.table {
width: 100%;
font-size: 14px;
}
.red {
color: #ff0000;
}
.mr10 {
margin-right: 10px;
}
.table-td-logo {
display: block;
margin: auto;
width: 40px;
height: 40px;
}
</style>
图片封装 图片.Vue
<template>
<el-upload
class="avatar-uploader"
action="http://localhost:8080/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="imgUrl" :src="imgUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</template>
<script>
export default {
name: "SingleUpload",
props:{
value:String
},
computed:{
imgUrl(){
return this.value;
}
},
data() {
return {
//定义数组接值
imageUrl:[],
}
},
methods: {
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!');
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!');
}
return isJPG && isLt2M;
},
handleAvatarSuccess(res, file) {
this.imageUrl.push(res.data);
//传值
localStorage.setItem('imageUrl', this.imageUrl);
this.emitInput(res.data);
},
emitInput(val){
this.$emit('input',val);
}
},
mounted() {
}
}
</script>
<style >
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 90px;
height: 90px;
display: block;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 90px;
height: 90px;
line-height: 90px;
text-align: center;
}
.avatar {
width: 90px;
height: 90px;
display: block;
}
</style>
请求代码brand.js页面:
import request from '../../../utils/request';
//查询数据
export const queryBrandList = query => {
return request({
url: 'http://localhost:8080/brand',
method: 'get',
params: query
});
};
//修改是否显示
export const updateShowStatus = query => {
return request({
url:'http://localhost:8080/brand/showStatus',
method: 'post',
params: query
});
};
//修改是否品牌供应商
export const updateFactoryStatus = query => {
return request({
url:'http://localhost:8080/brand/factoryStatus',
method: 'post',
params: query
});
};
//修改或新增
export const saveOrUpdate = query => {
return request({
url:'http://localhost:8080/brand/saveOrUpdate',
method: 'post',
params: query
});
};
//删除
export const deleteBrand = row => {
return request({
url:'http://localhost:8080/brand',
method: 'delete',
params: row
});
};
//批量删除
export const deleteBath = ids => {
return request({
url:'http://localhost:8080/brand/deleteBath',
method: 'post',
params: ids
});
};
//点击取消是批量删除已上传的图片
export const deleteFile = images=> {
return request({
url:'http://localhost:8080/upload/deleteFile',
method: 'post',
params: images
});
};
//回显数据
export const seleteBrandById = id => {
return request({
url: 'http://localhost:8080/brand/'+id,
method: 'get',
});
};
request.js页面
import axios from 'axios';
const service = axios.create({
timeout: 5000
});
export default service;