功能示範
我們要實作的功能如下,有兩個按鈕,點選第一個按鈕選擇檔案,選擇檔案後點選第二個按鈕上傳到伺服器。
功能需求:
- 隻允許上傳
、png
格式的圖檔jpg/jpeg
- 沒有上傳圖檔時顯示占位圖
- 選擇完圖檔後在頁面中渲染出來
建構頁面
template
<template>
<div>
<div>
<!-- 選擇圖檔後展示待上傳的圖檔 -->
<img v-if="base64img" :src="base64img" />
<!-- ↓ 沒有選擇時顯示占位圖 -->
<img v-else="base64img" src="@/assets/placeholderImg.png" />
</div>
<!-- 限制最大待上傳的檔案數量為 1 -->
<!-- 添加選擇檔案後的回調和移除待上傳檔案的回調 -->
<a-upload :before-upload="beforeUpload" :max-count="1" @remove="handleRemove">
<a-button>
<upload-outlined></upload-outlined>
Select File
</a-button>
</a-upload>
<!-- 點選上傳按鈕 -->
<a-button @click="triggerUpload" :loading="btnLoading">上傳</a-button>
</div>
邏輯部分
import { UploadOutlined } from '@ant-design/icons-vue'
import fruitApi from "@/api/fruitApi";
import {ref} from "vue";
import {Upload} from "ant-design-vue";
import {Notice} from "@/utils/interfaces";
const fileList = ref();
// ↑ 待上傳檔案清單 (其實裡面也就能放一個檔案)
const base64img = ref();
// ↑ 我們在頁面中顯示待上傳的圖檔的原理是
// 将所選擇的本地圖檔轉換為 Base64 編碼
// 之後讓圖檔的 src = base64img 來實作的
// 是以 base64img 就是一個儲存圖檔編碼的字元串
const btnLoading = ref<boolean>(false);
// ↑ 上傳按鈕的 loading 狀态
// 在點選上傳 至 擷取伺服器回調的時間内 按鈕不可用
/**
* 對圖檔編碼的函數
*/
function getBase64(img: Blob, callback: (base64Url: string) => void) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(<string>reader.result));
// ↑ 監聽 reader 的 load 事件,觸發時執行回調函數
// 回調函數的作用就是将圖檔編碼的base64字元串傳給 base64img
reader.readAsDataURL(img);
}
/**
* andv 上傳檔案的回調
* 傳回 false 則不會上傳檔案,而是由我們手動上傳
* 傳回 Upload.LIST_IGNORE 會取消将檔案添加至待上傳清單
* @param file 所選擇的檔案
*/
function beforeUpload(file: any) {
console.log(file);
// 判斷檔案是否為圖檔格式
const isImg = (file.type === 'image/jpeg' || file.type === 'image/png');
// 如果檔案不是圖檔格式則禁止上傳
if (!isImg) {
Notice.error("隻能上傳 jpeg/jpg/png 格式的檔案!");
// ↑ Notice.error 是我自己寫的 Notice 類的一個靜态函數
// 用的是 antdv 的 Notifiacation
return Upload.LIST_IGNORE;
}
fileList.value = file;
// ↓ 擷取所需要上傳圖檔的 Base64 編碼
getBase64(fileList.value, (cb_img: string) => {
// 将擷取到的所要上傳圖檔的 Base64 編碼渲染到圖檔上
base64img.value = cb_img;
})
return false;
}
/**
* 點選上傳按鈕的回調
*/
function triggerUpload() {
const formData = new FormData();
// 對檔案清單 判空
if (fileList.value != null) {
formData.append("file", fileList.value as any);
} else {
Notice.error("檔案不能為空!")
return Upload.LIST_IGNORE;
}
// ↓ 在上傳過程中,按鈕為 loading 狀态
btnLoading.value = true;
// 請求上傳接口
fruitApi({
method: 'put',
url: 'api/upload',
data: formData
})
.then((resp: any) => {
console.log(resp);
if (resp.code === 1) {
Notice.success(<string>resp.data);
changeUploadItemColor("#49aa19");
} else {
Notice.error("上傳失敗~");
changeUploadItemColor("#ff4d4f");
// ↑ 下面會提到這個函數
}
btnLoading.value = false;
})
.catch((err) => {
console.log(err);
Notice.error("發生了錯誤~");
btnLoading.value = false;
changeUploadItemColor("#ff4d4f");
})
}
/**
* 待上傳圖檔被删除的回調
*/
function handleRemove() {
fileList.value = null;
// ↑ 置空待上傳檔案清單
base64img.value = null;
// ↑ 顯示預設占位圖
}
/**
* 上傳圖檔成功或失敗後 list 變色的函數
*/
type uploadItemColor = "#49aa19" | "#ff4d4f";
// ↑ 兩個顔色 綠 和 紅
function changeUploadItemColor(color: uploadItemColor) {
const uploadListItem = document.querySelector(".ant-upload-list-item-name");
// ↑ 類名是 antdv 規定的,選擇這個類名即可
(uploadListItem as any).style.color = color;
// ↑ 使item文字變色
}