7.轮播图管理功能实现

飞一样的编程
飞一样的编程
擅长邻域:Java,MySQL,Linux,nginx,springboot,mongodb,微信小程序,vue

分类: Java springboot uni-app 专栏: 带小白springboot3+vue3+uniapp 标签: springboot3 vue3 uniapp

2026-01-17 15:58:32 113浏览

轮播图管理功能实现

讲之前把那个登录背景图处理下,5M 实在是太大了,截图一下换成小点的图

效果图

mybatisPlus 分页实现

https://jf3q.com/article/detail/11025

文件上传功能

https://jf3q.com/article/detail/10849

文件上传工具类

package com.jf3q.schoolbbs.utils;

import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * 这是一个文件上传的工具类
 */
public class UploadFile {
    
    // 允许上传的文件类型
    private static final List<String> ALLOWED_FILE_TYPES = Arrays.asList(
        "jpg", "jpeg", "png", "gif", "bmp", "webp", // 图片类型
        "doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf", // 文档类型
        "txt", "md", // 文本类型
        "zip", "rar", "7z", "tar", "gz" // 压缩文件类型
    );
    
    // 最大文件大小 (10MB)
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024;
    
    /**
     * 保存上传的文件
     * @param file 上传的文件
     * @param uploadDir 上传目录
     * @return 保存后的文件名
     * @throws IOException IO异常
     */
    public static String saveFile(MultipartFile file, String uploadDir) throws IOException {
        // 验证文件是否为空
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传的文件不能为空");
        }
        
        // 验证文件大小
        if (file.getSize() > MAX_FILE_SIZE) {
            throw new IllegalArgumentException("文件大小不能超过10MB");
        }
        
        // 获取文件扩展名
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        
        String extension = getFileExtension(originalFilename);
        
        // 验证文件类型
        if (!ALLOWED_FILE_TYPES.contains(extension.toLowerCase())) {
            throw new IllegalArgumentException("不支持的文件类型: " + extension);
        }
        
        // 创建上传目录(如果不存在)
        Path uploadPath = Paths.get(uploadDir);
        if (!Files.exists(uploadPath)) {
            Files.createDirectories(uploadPath);
        }
        
        // 生成唯一文件名
        String newFilename = UUID.randomUUID().toString() + "." + extension;
        Path filePath = uploadPath.resolve(newFilename);
        
        // 保存文件
        Files.copy(file.getInputStream(), filePath);
        
        return newFilename;
    }
    
    /**
     * 删除文件
     * @param filename 文件名
     * @param uploadDir 上传目录
     * @return 是否删除成功
     */
    public static boolean deleteFile(String filename, String uploadDir) {
        try {
            Path filePath = Paths.get(uploadDir, filename);
            return Files.deleteIfExists(filePath);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 获取文件扩展名
     * @param filename 文件名
     * @return 扩展名
     */
    private static String getFileExtension(String filename) {
        int lastIndexOf = filename.lastIndexOf(".");
        if (lastIndexOf == -1) {
            return ""; // 无扩展名
        }
        return filename.substring(lastIndexOf + 1);
    }
    
    /**
     * 验证文件类型是否被允许
     * @param filename 文件名
     * @return 是否允许
     */
    public static boolean isAllowedFileType(String filename) {
        if (filename == null) {
            return false;
        }
        String extension = getFileExtension(filename);
        return ALLOWED_FILE_TYPES.contains(extension.toLowerCase());
    }
    
    /**
     * 验证文件大小是否在限制范围内
     * @param size 文件大小(字节)
     * @return 是否在范围内
     */
    public static boolean isAllowedFileSize(long size) {
        return size > 0 && size <= MAX_FILE_SIZE;
    }
}

前端开发

https://element-plus.org/zh-CN/

最终的效果如下:

最重要的就是注意图片上传和图片显示的时候的反向代理

const baseUrl=process.env.VUE_APP_BASE_API

完整的前端代码如下,供大家参考

<script setup>
import {onMounted, ref} from "vue";
import {getBannerPage, addBanner, delBanner} from "@/api";
import {ElMessage} from 'element-plus'
import {Plus, Edit, Delete} from '@element-plus/icons-vue'
const baseUrl=process.env.VUE_APP_BASE_API
const pageNum=ref(1)
const tableData = ref([])
const dialogVisible = ref(false)
const bannerForm = ref({
  img: ''
})
const pages=ref(0)
const total=ref(0)
const changePageNum=async (val)=>{
  pageNum.value=val
  await getPage()

}
const getPage = async () => {
  const res = await getBannerPage(pageNum.value)
  tableData.value = res.data.records
  total.value=res.data.total
}
const handleDelete = async(id) => {
  const res = await delBanner(id)
  console.log(res)
  await getPage()


}

const handleAdd = () => {
  bannerForm.value = {
    img: ''
  }
  dialogVisible.value = true
}

const handleEdit = (row) => {
  bannerForm.value = {
    img: row.img
  }
  dialogVisible.value = true
}


const beforeUpload = (file) => {
  const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
  const isLt2M = file.size / 1024 / 1024 < 2

  if (!isJPG) {
    ElMessage.error('上传图片只能是 JPG/PNG 格式!')
  }
  if (!isLt2M) {
    ElMessage.error('上传图片大小不能超过 2MB!')
  }
  return isJPG && isLt2M
}

const handleUploadSuccess = (response) => {
  bannerForm.value.img = response.data
  ElMessage.success('上传成功')
}
const uploadAction=baseUrl+"/admin/banner/upload"

const saveBanner = async () => {
  if (!bannerForm.value.img) {
    ElMessage.warning('请上传图片')
    return
  }
  try {
    await addBanner(bannerForm.value)
    ElMessage.success('添加成功')
    dialogVisible.value = false
    await getPage()
  } catch (error) {
    ElMessage.error('添加失败')
  }
}

onMounted(async ()=>{
  const res = await getBannerPage(pageNum.value)
  console.log(res);
  tableData.value=res.data.records
  total.value=res.data.total
  pages.value=res.data.pages
})
</script>

<template>
<div class="banner-container">
  <!-- 页面头部 -->
  <div class="page-header">
    <div class="header-title">
      <h2>轮播图管理</h2>
      <p class="subtitle">管理系统首页轮播图展示</p>
    </div>
    <el-button type="primary" @click="handleAdd" class="add-btn">
      <el-icon><Plus /></el-icon>
      新增轮播图
    </el-button>
  </div>

  <!-- 表格区域 -->
  <div class="table-wrapper">
    <el-table :data="tableData" class="banner-table" stripe>
      <el-table-column prop="id" label="ID" width="80" align="center" />
      <el-table-column label="图片" width="200" align="center">
        <template #default="scope">
          <el-image 
            :src="baseUrl+'/banner/'+scope.row.img" 
            class="banner-image"
            fit="cover"
            :preview-src-list="[baseUrl+'/banner/'+scope.row.img]"
            preview-teleported
          />
        </template>
      </el-table-column>
      <el-table-column prop="createTime" label="添加时间" align="center" />
      <el-table-column label="操作" width="200" align="center">
        <template #default="scope">
          <el-button type="primary" link @click="handleEdit(scope.row)">
            <el-icon><Edit /></el-icon>
            编辑
          </el-button>
          <el-button type="danger" link @click="handleDelete(scope.row.id)">
            <el-icon><Delete /></el-icon>
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>

  <!-- 分页 -->
  <div class="pagination-wrapper">
    <el-pagination
      background
      layout="prev, pager, next"
      :page-count="pages"
      :total="total"
      @current-change="changePageNum"
      class="custom-pagination"
    />
  </div>

  <!-- 新增/编辑对话框 -->
  <el-dialog 
    v-model="dialogVisible" 
    :title="bannerForm.id ? '编辑轮播图' : '新增轮播图'" 
    width="600px"
    class="banner-dialog"
  >
    <el-form :model="bannerForm" label-width="100px" class="banner-form">
      <el-form-item label="上传图片">
        <el-upload
          class="avatar-uploader"
          :action="uploadAction"
          :show-file-list="false"
          :on-success="handleUploadSuccess"
          :before-upload="beforeUpload">
          <img v-if="bannerForm.img" :src="baseUrl+'/banner/'+bannerForm.img" class="avatar" />
          <div v-else class="upload-placeholder">
            <el-icon class="upload-icon"><Plus /></el-icon>
            <div class="upload-text">点击上传图片</div>
          </div>
        </el-upload>
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveBanner">确定</el-button>
      </div>
    </template>
  </el-dialog>
</div>
</template>

<style scoped lang="scss">
.banner-container {
  padding: 20px;
  background-color: #f5f7fa;
  min-height: 100vh;
}

// 页面头部
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding: 20px;
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);

  .header-title {
    h2 {
      margin: 0;
      color: #ffffff;
      font-size: 24px;
      font-weight: 600;
    }

    .subtitle {
      margin: 8px 0 0 0;
      color: rgba(255, 255, 255, 0.85);
      font-size: 14px;
    }
  }

  .add-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 12px 24px;
    font-size: 14px;
    border-radius: 6px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    transition: all 0.3s;

    &:hover {
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    }
  }
}

// 表格区域
.table-wrapper {
  background: #ffffff;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  margin-bottom: 20px;

  .banner-table {
    :deep(.el-table__header-wrapper) {
      th {
        background-color: #f8f9fa;
        color: #333;
        font-weight: 600;
      }
    }
  }

  .banner-image {
    width: 120px;
    height: 80px;
    border-radius: 4px;
    cursor: pointer;
    transition: transform 0.3s;

    &:hover {
      transform: scale(1.05);
    }
  }
}

// 分页
.pagination-wrapper {
  display: flex;
  justify-content: center;
  padding: 20px;
  background: #ffffff;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);

  .custom-pagination {
    :deep(.el-pagination.is-background .el-pager li:not(.is-disabled).is-active) {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    }
  }
}

// 对话框
.banner-dialog {
  :deep(.el-dialog__header) {
    padding: 20px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: #ffffff;
    margin: 0;
    border-radius: 8px 8px 0 0;

    .el-dialog__title {
      color: #ffffff;
      font-size: 18px;
      font-weight: 600;
    }

    .el-dialog__headerbtn {
      .el-dialog__close {
        color: #ffffff;
        font-size: 18px;

        &:hover {
          color: #ffffff;
        }
      }
    }
  }

  .banner-form {
    padding: 20px;
  }

  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
}

// 上传组件
.avatar-uploader {
  .avatar {
    width: 200px;
    height: 150px;
    display: block;
    border-radius: 4px;
    object-fit: cover;
  }

  :deep(.el-upload) {
    border: 2px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: all 0.3s;
    width: 200px;
    height: 150px;

    &:hover {
      border-color: #667eea;
    }
  }
}

.upload-placeholder {
  width: 200px;
  height: 150px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #8c939d;

  .upload-icon {
    font-size: 32px;
    margin-bottom: 8px;
  }

  .upload-text {
    font-size: 14px;
  }
}
</style>

一键三连支持下杰哥哦,免费送资料

v:jf3qcom


好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695