** **
**一、**對象存儲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
}