阿里雲OSS、EsayExcel

** **

**一、**對象存儲OSS

爲了解決海量數據存儲與彈性擴容,項目中我們採用雲存儲的解決方案- 阿里雲OSS。

1、開通“對象存儲OSS”服務

(1)申請阿里雲賬號

(2)實名認證

(3)開通“對象存儲OSS”服務

(4)進入管理控制檯

2、創建Bucket

選擇:標準存儲、公共讀、不開通

在這裏插入圖片描述

3、上傳默認頭像

創建文件夾avatar,上傳默認的用戶頭像

在這裏插入圖片描述

4、創建RAM子用戶

在這裏插入圖片描述

二、使用SDK

在這裏插入圖片描述

1、創建Mavaen項目

com.atguigu

aliyun-oss

2、pom

<dependencies>
    <!--aliyunOSS-->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>2.8.3</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

3、找到編碼時需要用到的常量值

(1)endpoint

(2)bucketName

(3)accessKeyId

(4)accessKeySecret

4、測試創建Bucket的連接

在這裏插入圖片描述

package com.atguigu.oss;

public class OSSTest {

    // Endpoint以杭州爲例,其它Region請按實際情況填寫。
    String endpoint = "your endpoint";
    // 阿里雲主賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建並使用RAM賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建RAM賬號。
    String accessKeyId = "your accessKeyId";
    String accessKeySecret = "your accessKeySecret";
    String bucketName = "guli-file";

    @Test
    public void testCreateBucket() {

        // 創建OSSClient實例。
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

        // 創建存儲空間。
        ossClient.createBucket(bucketName);

        // 關閉OSSClient。
        ossClient.shutdown();
    }
}

5、判斷存儲空間是否存在

在這裏插入圖片描述

@Test
public void testExist() {

    // 創建OSSClient實例。
    OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

    boolean exists = ossClient.doesBucketExist(bucketName);
    System.out.println(exists);

    // 關閉OSSClient。
    ossClient.shutdown();
}

6、設置存儲空間的訪問權限

@Test
public void testAccessControl() {

    // 創建OSSClient實例。
    OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

    // 設置存儲空間的訪問權限爲:公共讀。
    ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);

    // 關閉OSSClient。
    ossClient.shutdown();
}

在這裏插入圖片描述


一、新建雲存儲微服務

1、在service模塊下創建子模塊service-oss

在這裏插入圖片描述

2、配置pom.xml

service-oss上級模塊service已經引入service的公共依賴,所以service-oss模塊只需引入阿里雲oss相關依賴即可,

service父模塊已經引入了service-base模塊,所以Swagger相關默認已經引入

<dependencies>
    <!-- 阿里雲oss依賴 -->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
    </dependency>

    <!-- 日期工具欄依賴 -->
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
    </dependency>
</dependencies>

3、配置application.properties

#服務端口
server.port=8002
#服務名
spring.application.name=service-oss

#環境設置:dev、test、prod
spring.profiles.active=dev
        
#阿里雲 OSS
#不同的服務器,地址不同
aliyun.oss.file.endpoint=your endpoint
aliyun.oss.file.keyid=your accessKeyId
aliyun.oss.file.keysecret=your accessKeySecret
#bucket可以在控制檯創建,也可以使用java代碼創建
aliyun.oss.file.bucketname=guli-file

4、logback-spring.xml

5、創建啓動類

創建OssApplication.java

package com.guli.oss;
@SpringBootApplication
@ComponentScan({"com.atguigu"})
public class OssApplication {

    public static void main(String[] args) {
        SpringApplication.run(OssApplication.class, args);
    }
}

6、啓動項目

報錯

加粗樣式
spring boot 會默認加載org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration這個類,

而DataSourceAutoConfiguration類使用了@Configuration註解向spring注入了dataSource bean,又因爲項目(oss模塊)中並沒有關於dataSource相關的配置信息,所以當spring創建dataSource bean時因缺少相關的信息就會報錯。

解決辦法:

方法1、在@SpringBootApplication註解上加上exclude,解除自動加載DataSourceAutoConfiguration

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

二、實現文件上傳

1、從配置文件讀取常量

創建常量讀取工具類:ConstantPropertiesUtil.java

使用@Value讀取application.properties裏的配置內容

用spring的 InitializingBean 的 afterPropertiesSet 來初始化配置信息,這個方法將在所有的屬性被初始化後調用。

/**
 * 常量類,讀取配置文件application.properties中的配置
 */
@Component
//@PropertySource("classpath:application.properties")
public class ConstantPropertiesUtil implements InitializingBean {

    @Value("${aliyun.oss.file.endpoint}")
    private String endpoint;

    @Value("${aliyun.oss.file.keyid}")
    private String keyId;

    @Value("${aliyun.oss.file.keysecret}")
    private String keySecret;

    @Value("${aliyun.oss.file.filehost}")
    private String fileHost;

    @Value("${aliyun.oss.file.bucketname}")
    private String bucketName;

    public static String END_POINT;
    public static String ACCESS_KEY_ID;
    public static String ACCESS_KEY_SECRET;
    public static String BUCKET_NAME;
    public static String FILE_HOST ;

    @Override
    public void afterPropertiesSet() throws Exception {
        END_POINT = endpoint;
        ACCESS_KEY_ID = keyId;
        ACCESS_KEY_SECRET = keySecret;
        BUCKET_NAME = bucketName;
        FILE_HOST = fileHost;
    }
}

2、文件上傳

創建Service接口:FileService.java

public interface FileService {

    /**
     * 文件上傳至阿里雲
     * @param file
     * @return
     */
    String upload(MultipartFile file);
}

實現:FileServiceImpl.java

參考SDK中的:Java->上傳文件->簡單上傳->流式上傳->上傳文件流

在這裏插入圖片描述

public class FileServiceImpl implements FileService {

    @Override
    public String upload(MultipartFile file) {

        //獲取阿里雲存儲相關常量
        String endPoint = ConstantPropertiesUtil.END_POINT;
        String accessKeyId = ConstantPropertiesUtil.ACCESS_KEY_ID;
        String accessKeySecret = ConstantPropertiesUtil.ACCESS_KEY_SECRET;
        String bucketName = ConstantPropertiesUtil.BUCKET_NAME;
        String fileHost = ConstantPropertiesUtil.FILE_HOST;

        String uploadUrl = null;

        try {
            //判斷oss實例是否存在:如果不存在則創建,如果存在則獲取
            OSSClient ossClient = new OSSClient(endPoint, accessKeyId, accessKeySecret);
            if (!ossClient.doesBucketExist(bucketName)) {
                //創建bucket
                ossClient.createBucket(bucketName);
                //設置oss實例的訪問權限:公共讀
                ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
            }

            //獲取上傳文件流
            InputStream inputStream = file.getInputStream();

            //構建日期路徑:avatar/2019/02/26/文件名
            String filePath = new DateTime().toString("yyyy/MM/dd");

            //文件名:uuid.擴展名
            String original = file.getOriginalFilename();
            String fileName = UUID.randomUUID().toString();
            String fileType = original.substring(original.lastIndexOf("."));
            String newName = fileName + fileType;
            String fileUrl = fileHost + "/" + filePath + "/" + newName;

            //文件上傳至阿里雲
            ossClient.putObject(bucketName, fileUrl, inputStream);

            // 關閉OSSClient。
            ossClient.shutdown();

            //獲取url地址
            uploadUrl = "http://" + bucketName + "." + endPoint + "/" + fileUrl;

        } catch (IOException e) {
            throw new GuliException(ResultCodeEnum.FILE_UPLOAD_ERROR);
        }

        return uploadUrl;
    }
}

3、控制層

創建controller:FileUploadController.java

package com.guli.oss.controller;

@Api(description="阿里雲文件管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/oss/file")
public class FileController {

    @Autowired
    private FileService fileService;

    /**
     * 文件上傳
     *
     * @param file
     */
    @ApiOperation(value = "文件上傳")
    @PostMapping("upload")
    public R upload(
            @ApiParam(name = "file", value = "文件", required = true)
            @RequestParam("file") MultipartFile file) {

        String uploadUrl = fileService.upload(file);
        //返回r對象
        return R.ok().message("文件上傳成功").data("url", uploadUrl);

    }
}

4、重啓oss服務

5、Swagger中測試文件上傳

6、配置nginx反向代理

將接口地址加入nginx配置

location ~ /eduoss/ {           
    proxy_pass http://localhost:8001;
}

一、前端整合圖片上傳組件

1、複製頭像上傳組件

從vue-element-admin複製組件:

vue-element-admin/src/components/ImageCropper

vue-element-admin/src/components/PanThumb

2、前端參考實現

src/views/components-demo/avatarUpload.vue

3、前端添加文件上傳組件

src/views/edu/teacher/form.vue

template:

<!-- 講師頭像 -->
<el-form-item label="講師頭像">

    <!-- 頭銜縮略圖 -->
    <pan-thumb :image="teacher.avatar"/>
    <!-- 文件上傳按鈕 -->
    <el-button type="primary" icon="el-icon-upload" @click="imagecropperShow=true">更換頭像
    </el-button>

    <!--
v-show:是否顯示上傳組件
:key:類似於id,如果一個頁面多個圖片上傳控件,可以做區分
:url:後臺上傳的url地址
@close:關閉上傳組件
@crop-upload-success:上傳成功後的回調 -->
    <image-cropper
                   v-show="imagecropperShow"
                   :width="300"
                   :height="300"
                   :key="imagecropperKey"
                   :url="BASE_API+'/admin/oss/file/upload'"
                   field="file"
                   @close="close"
                   @crop-upload-success="cropSuccess"/>

</el-form-item>

引入組件模塊

import ImageCropper from '@/components/ImageCropper'
import PanThumb from '@/components/PanThumb'

4、設置默認頭像

config/dev.env.js中添加阿里雲oss bucket地址

OSS_PATH: '"https://guli-file.oss-cn-beijing.aliyuncs.com"'

組件中初始化頭像默認地址

const defaultForm = {
  ......,
  avatar: process.env.OSS_PATH + '/avatar/default.jpg'
}

5、js腳本實現上傳和圖片回顯

export default {
  components: { ImageCropper, PanThumb },
  data() {
    return {
      //其它數據模型
      ......,
        
      BASE_API: process.env.BASE_API, // 接口API地址
      imagecropperShow: false, // 是否顯示上傳組件
      imagecropperKey: 0 // 上傳組件id
    }
  },
    
  ......,
    
  methods: {
    //其他函數
    ......,

    // 上傳成功後的回調函數
    cropSuccess(data) {
      console.log(data)
      this.imagecropperShow = false
      this.teacher.avatar = data.url
      // 上傳成功後,重新打開上傳組件時初始化組件,否則顯示上一次的上傳結果
      this.imagecropperKey = this.imagecropperKey + 1
    },

    // 關閉上傳組件
    close() {
      this.imagecropperShow = false
      // 上傳失敗後,重新打開上傳組件時初始化組件,否則顯示上一次的上傳結果
      this.imagecropperKey = this.imagecropperKey + 1
    }
  }
}

二、測試文件上傳

前後端聯調


一、Excel導入導出的應用場景

1、數據導入:減輕錄入工作量

2、數據導出:統計信息歸檔

3、數據傳輸:異構系統之間數據傳輸

二、EasyExcel簡介

1、EasyExcel特點

  • Java領域解析、生成Excel比較有名的框架有Apache poi、jxl等。但他們都存在一個嚴重的問題就是非常的耗內存。如果你的系統併發量不大的話可能還行,但是一旦併發上來後一定會OOM或者JVM頻繁的full gc。
  • EasyExcel是阿里巴巴開源的一個excel處理框架,以使用簡單、節省內存著稱。EasyExcel能大大減少佔用內存的主要原因是在解析Excel時沒有將文件數據一次性全部加載到內存中,而是從磁盤上一行行讀取數據,逐個解析。
  • EasyExcel採用一行一行的解析模式,並將一行的解析結果以觀察者的模式通知處理(AnalysisEventListener)。

一、創建項目,實現EasyExcel對Excel寫操作

1、創建一個普通的maven項目

項目名:excel-easydemo

2、pom中引入xml相關依賴

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.1.1</version>
    </dependency>
</dependencies>

3、創建實體類

設置表頭和添加的數據字段

import com.alibaba.excel.annotation.ExcelProperty;

//設置表頭和添加的數據字段
public class DemoData {
    //設置表頭名稱
    @ExcelProperty("學生編號")
    private int sno;
    
    //設置表頭名稱
    @ExcelProperty("學生姓名")
    private String sname;

    public int getSno() {
        return sno;
    }

    public void setSno(int sno) {
        this.sno = sno;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    @Override
    public String toString() {
        return "DemoData{" +
                "sno=" + sno +
                ", sname='" + sname + '\'' +
                '}';
    }
}

4 、實現寫操作

(1)創建方法循環設置要添加到Excel的數據

//循環設置要添加的數據,最終封裝到list集合中
private static List<DemoData> data() {
    List<DemoData> list = new ArrayList<DemoData>();
    for (int i = 0; i < 10; i++) {
        DemoData data = new DemoData();
        data.setSno(i);
        data.setSname("張三"+i);
        list.add(data);
    }
    return list;
}

(2)實現最終的添加操作(寫法一)

public static void main(String[] args) throws Exception {
    // 寫法1
    String fileName = "F:\\11.xlsx";
    // 這裏 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字爲模板 然後文件流會自動關閉
    // 如果這裏想使用03 則 傳入excelType參數即可
    EasyExcel.write(fileName, DemoData.class).sheet("寫入方法一").doWrite(data());
}

(3)實現最終的添加操作(寫法二)

public static void main(String[] args) throws Exception {
    // 寫法2,方法二需要手動關閉流
    String fileName = "F:\\112.xlsx";
    // 這裏 需要指定寫用哪個class去寫
    ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
    WriteSheet writeSheet = EasyExcel.writerSheet("寫入方法二").build();
    excelWriter.write(data(), writeSheet);
    /// 千萬別忘記finish 會幫忙關閉流
    excelWriter.finish();
}

一、實現EasyExcel對Excel讀操作

1、創建實體類

import com.alibaba.excel.annotation.ExcelProperty;
public class ReadData {
    //設置列對應的屬性
    @ExcelProperty(index = 0)
    private int sid;
    
    //設置列對應的屬性
    @ExcelProperty(index = 1)
    private String sname;

    public int getSid() {
        return sid;
    }
    public void setSid(int sid) {
        this.sid = sid;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    @Override
    public String toString() {
        return "ReadData{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                '}';
    }
}

2、創建讀取操作的監聽器

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

//創建讀取excel監聽器
public class ExcelListener extends AnalysisEventListener<ReadData> {

    //創建list集合封裝最終的數據
    List<ReadData> list = new ArrayList<ReadData>();

    //一行一行去讀取excle內容
    @Override
    public void invoke(ReadData user, AnalysisContext analysisContext) {
       System.out.println("***"+user);
        list.add(user);
    }

    //讀取excel表頭信息
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表頭信息:"+headMap);
    }

    //讀取完成後執行
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    }
}

3、調用實現最終的讀取

   public static void main(String[] args) throws Exception {

        // 寫法1:
        String fileName = "F:\\01.xlsx";
        // 這裏 需要指定讀用哪個class去讀,然後讀取第一個sheet 文件流會自動關閉
        EasyExcel.read(fileName, ReadData.class, new ExcelListener()).sheet().doRead();

        // 寫法2:
        InputStream in = new BufferedInputStream(new FileInputStream("F:\\01.xlsx"));
        ExcelReader excelReader = EasyExcel.read(in, ReadData.class, new ExcelListener()).build();
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        excelReader.read(readSheet);
        // 這裏千萬別忘記關閉,讀的時候會創建臨時文件,到時磁盤會崩的
        excelReader.finish();
}

一、Excel模板

1、編輯Excel模板

2、將文件上傳至阿里雲OSS

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ntAEjH0g-1586335226848)(file:///D:/我的文檔/Documents/My Knowledge/temp/a45d8977-7752-48af-a0ec-a0b63eb31e5c/128/index_files/673ed30a-365a-42b5-9143-e8b44fd15cfc.png)]

二、配置路由

1、添加路由

// 課程分類管理
{
  path: '/edu/subject',
  component: Layout,
  redirect: '/edu/subject/list',
  name: 'Subject',
  meta: { title: '課程分類管理', icon: 'nested' },
  children: [
    {
      path: 'list',
      name: 'EduSubjectList',
      component: () => import('@/views/edu/subject/list'),
      meta: { title: '課程分類列表' }
    },
    {
      path: 'import',
      name: 'EduSubjectImport',
      component: () => import('@/views/edu/subject/import'),
      meta: { title: '導入課程分類' }
    }
  ]
},

2、添加vue組件

在這裏插入圖片描述

三、表單組件import.vue1、js定義數據

<script>
export default {

  data() {
    return {
      BASE_API: process.env.BASE_API, // 接口API地址
      OSS_PATH: process.env.OSS_PATH, // 阿里雲OSS地址
      fileUploadBtnText: '上傳到服務器', // 按鈕文字
      importBtnDisabled: false, // 按鈕是否禁用,
      loading: false
    }
  }
}
</script>

2、template

<template>
  <div class="app-container">
    <el-form label-width="120px">
      <el-form-item label="信息描述">
        <el-tag type="info">excel模版說明</el-tag>
        <el-tag>
          <i class="el-icon-download"/>
          <a :href="OSS_PATH + '/excel/%E8%AF%BE%E7%A8%8B%E5%88%86%E7%B1%BB%E5%88%97%E8%A1%A8%E6%A8%A1%E6%9D%BF.xls'">點擊下載模版</a>
        </el-tag>

      </el-form-item>

      <el-form-item label="選擇Excel">
        <el-upload
          ref="upload"
          :auto-upload="false"
          :on-success="fileUploadSuccess"
          :on-error="fileUploadError"
          :disabled="importBtnDisabled"
          :limit="1"
          :action="BASE_API+'/admin/edu/subject/import'"
          name="file"
          accept="application/vnd.ms-excel">
          <el-button slot="trigger" size="small" type="primary">選取文件</el-button>
          <el-button
            :loading="loading"
            style="margin-left: 10px;"
            size="small"
            type="success"
            @click="submitUpload">{{ fileUploadBtnText }}</el-button>
        </el-upload>
      </el-form-item>
    </el-form>
  </div>
</template>

3、js上傳方法

methods: {
    submitUpload() {
      this.fileUploadBtnText = '正在上傳'
      this.importBtnDisabled = true
      this.loading = true
      this.$refs.upload.submit()
    },

    fileUploadSuccess(response) {
      
    },

    fileUploadError(response) {
      
    }
}

4、回調函數

fileUploadSuccess(response) {
    if (response.success === true) {
    this.fileUploadBtnText = '導入成功'
    this.loading = false
    this.$message({
        type: 'success',
        message: response.message
    })
    } 
},

fileUploadError(response) {
    this.fileUploadBtnText = '導入失敗'
    this.loading = false
    this.$message({
    type: 'error',
    message: '導入失敗'
    })
}

一、添加依賴

1、service-edu模塊****配置依賴

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.1.1</version>
    </dependency>
</dependencies>

二、業務處理

1、SubjectAdminController

package com.guli.edu.controller.admin;
@Api(description="課程分類管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/eduservice/subject")
public class SubjectAdminController {

    @Autowired
    private SubjectService subjectService;

    //添加課程分類
    @ApiOperation(value = "Excel批量導入")
    @PostMapping("addSubject")
    public R addSubject(MultipartFile file) {
        //1 獲取上傳的excel文件 MultipartFile
        //返回錯誤提示信息
        subjectService.importSubjectData(file,subjectService);
        //判斷返回集合是否爲空
        return R.ok();
    }
}

2、創建和Excel對應的實體類

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

@Data
public class ExcelSubjectData {
    @ExcelProperty(index = 0)
    private int oneSubjectName;

    @ExcelProperty(index = 1)
    private String twoSubjectName;
}

3、SubjectService

(1)接口

void batchImport(MultipartFile file);

(2)實現類

//添加課程分類
//poi讀取excel內容
@Override
public void importSubjectData(MultipartFile file,EduSubjectService subjectService) {
    try {
        //1 獲取文件輸入流
        InputStream inputStream = file.getInputStream();

        // 這裏 需要指定讀用哪個class去讀,然後讀取第一個sheet 文件流會自動關閉
        EasyExcel.read(inputStream, ExcelSubjectData.class, new SubjectExcelListener(subjectService)).sheet().doRead();
    }catch(Exception e) {
        e.printStackTrace();
        throw new GuliException(20002,"添加課程分類失敗");
    }
}

4、創建讀取Excel監聽器

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.atguigu.eduservice.entity.EduSubject;
import com.atguigu.eduservice.entity.vo.ExcelSubjectData;
import com.atguigu.eduservice.service.EduSubjectService;
import com.atguigu.servicebase.handler.GuliException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class SubjectExcelListener extends AnalysisEventListener<ExcelSubjectData> {

    public EduSubjectService subjectService;
    
    public SubjectExcelListener() {}
    //創建有參數構造,傳遞subjectService用於操作數據庫
    public SubjectExcelListener(EduSubjectService subjectService) {
        this.subjectService = subjectService;
    }

    //一行一行去讀取excle內容
    @Override
    public void invoke(ExcelSubjectData user, AnalysisContext analysisContext) {
        if(user == null) {
            throw new GuliException(20001,"添加失敗");
        }
        //添加一級分類
        EduSubject existOneSubject = this.existOneSubject(subjectService,user.getOneSubjectName());
        if(existOneSubject == null) {//沒有相同的
            existOneSubject = new EduSubject();
            existOneSubject.setTitle(user.getOneSubjectName());
            existOneSubject.setParentId("0");
            subjectService.save(existOneSubject);
        }

        //獲取一級分類id值
        String pid = existOneSubject.getId();
        
        //添加二級分類
        EduSubject existTwoSubject = this.existTwoSubject(subjectService,user.getTwoSubjectName(), pid);
        if(existTwoSubject == null) {
            existTwoSubject = new EduSubject();
            existTwoSubject.setTitle(user.getTwoSubjectName());
            existTwoSubject.setParentId(pid);
            subjectService.save(existTwoSubject);
        }
    }

    //讀取excel表頭信息
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表頭信息:"+headMap);
    }

    //讀取完成後執行
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {}

    //判斷一級分類是否重複
    private EduSubject existTwoSubject(EduSubjectService subjectService,String name,String pid) {
        QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
        wrapper.eq("title",name);
        wrapper.eq("parent_id",pid);
        EduSubject eduSubject = subjectService.getOne(wrapper);
        return eduSubject;
    }

    //判斷一級分類是否重複
    private EduSubject existOneSubject(EduSubjectService subjectService,String name) {
        QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
        wrapper.eq("title",name);
        wrapper.eq("parent_id","0");
        EduSubject eduSubject = subjectService.getOne(wrapper);
        return eduSubject;
    }
}

一、前端實現

1、參考 views/tree/index.vue

2、創建api

api/edu/subject.js

import request from '@/utils/request'

const api_name = '/admin/edu/subject'

export default {

  getNestedTreeList() {
    return request({
      url: `${api_name}`,
      method: 'get'
    })
  }
}

3、list.vue

<template>
  <div class="app-container">
    <el-input v-model="filterText" placeholder="Filter keyword" style="margin-bottom:30px;" />

    <el-tree
      ref="subjectTree"
      :data="subjectList"
      :props="defaultProps"
      :filter-node-method="filterNode"
      class="filter-tree"
      default-expand-all
    />

  </div>
</template>

<script>
import subject from '@/api/edu/subject'
export default {

  data() {
    return {
      filterText: '',
      subjectList: [],
      defaultProps: {
        children: 'children',
        label: 'title'
      }
    }
  },
  watch: {
    filterText(val) {
      this.$refs.subjectTree.filter(val)
    }
  },

  created() {
    this.fetchNodeList()
  },

  methods: {
    fetchNodeList() {
      subject.getNestedTreeList().then(response => {
        if (response.success === true) {
          this.subjectList = response.data.items
        }
      })
    },
    filterNode(value, data) {
      if (!value) return true
      return data.title.indexOf(value) !== -1
    }
  }
}
</script>

二、後端實現

1、創建vo

package com.guli.edu.vo;
@Data
public class SubjectVo {

    private String id;
    private String title;
}
package com.guli.edu.vo;
@Data
public class SubjectNestedVo {

    private String id;
    private String title;
    private List<SubjectVo> children = new ArrayList<>();
}

2、創建controller

@ApiOperation(value = "嵌套數據列表")
@GetMapping("")
public R nestedList(){

    List<SubjectNestedVo> subjectNestedVoList = subjectService.nestedList();
    return R.ok().data("items", subjectNestedVoList);
}

3、創建service

接口

List<SubjectNestedVo> nestedList();

實現Final

@Override
public List<SubjectNestedVo> nestedList() {

    //最終要的到的數據列表
    ArrayList<SubjectNestedVo> subjectNestedVoArrayList = new ArrayList<>();

    //獲取一級分類數據記錄
    QueryWrapper<Subject> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("parent_id", 0);
    queryWrapper.orderByAsc("sort", "id");
    List<Subject> subjects = baseMapper.selectList(queryWrapper);

    //獲取二級分類數據記錄
    QueryWrapper<Subject> queryWrapper2 = new QueryWrapper<>();
    queryWrapper2.ne("parent_id", 0);
    queryWrapper2.orderByAsc("sort", "id");
    List<Subject> subSubjects = baseMapper.selectList(queryWrapper2);

    //填充一級分類vo數據
    int count = subjects.size();
    for (int i = 0; i < count; i++) {
        Subject subject = subjects.get(i);

        //創建一級類別vo對象
        SubjectNestedVo subjectNestedVo = new SubjectNestedVo();
        BeanUtils.copyProperties(subject, subjectNestedVo);
        subjectNestedVoArrayList.add(subjectNestedVo);

        //填充二級分類vo數據
        ArrayList<SubjectVo> subjectVoArrayList = new ArrayList<>();
        int count2 = subSubjects.size();
        for (int j = 0; j < count2; j++) {

            Subject subSubject = subSubjects.get(j);
            if(subject.getId().equals(subSubject.getParentId())){

                //創建二級類別vo對象
                SubjectVo subjectVo = new SubjectVo();
                BeanUtils.copyProperties(subSubject, subjectVo);
                subjectVoArrayList.add(subjectVo);
            }
        }
        subjectNestedVo.setChildren(subjectVoArrayList);
    }


    return subjectNestedVoArrayList;
}

三、優化前端過濾功能

filterNode(value, data) {
    if (!value) return true
    return data.title.toLowerCase().indexOf(value.toLowerCase()) !== -1
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章