前端
與后端交互
下載后端接口的文件時,若是二進制,需要在請求中添加responseType: ‘blob’
例如
axios.get(‘http://127.0.0.1:8612/api/daily/report/tdjzxz?selectedMonth=2022-06’, {
headers: {
‘Accesstoken’: ‘f033b94655f84386a0c112b41a82583b’
},
responseType: ‘blob’
})
.then(res => {
console.log(res)
const blob = new Blob([res.data], { type: res.headers[‘content-type’] })
const a = document.createElement(‘a’)
const url = window.URL.createObjectURL(blob)
a.href = url
a.download = ‘file.xlsx’
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
document.body.removeChild(a)
})
.catch(error => {
console.log(error)
})
原因
在Vue中,使用get請求時,可以選擇是否帶上responseType參數。如果不帶上該參數,則默認返回的是JSON格式的數據。如果帶上參數responseType: ‘blob’,則返回的是二進制數據。
區別在于:
1.返回的數據格式不同:不帶參數時返回JSON格式的數據,帶參數時返回二進制數據。
2.處理方式不同:對于JSON格式的數據,可以直接使用Vue提供的方法進行解析和處理,而對于二進制數據,則需要使用其他方式進行處理,例如將其轉換為Blob對象或者使用FileReader進行讀取。
3.適用場景不同:如果需要下載文件或者獲取圖片等二進制數據,使用帶參數的方式比較合適;如果需要獲取JSON格式的數據,則使用不帶參數的方式即可。
在前后端分離的項目中,后端使用了springboot,前端使用了vue,現在有一個下載的需求,下載正常時二進制數據,不正常是包含提醒信息的json數據,前端應該怎么區分處理正常與不正常的數據?
可以在后端在返回數據時設置一個特定的HTTP響應頭,例如 Content-Type: application/json ,來指示返回的數據是JSON格式的。在前端中,可以檢查響應頭來判斷數據的類型。如果響應頭為 Content-Type: application/json ,則可以將響應體解析為JSON格式的數據,并根據數據中的字段進行相應的處理。如果響應頭為其他類型,則可以將響應體視為二進制數據進行處理。
另外,如果后端返回的數據在不正常情況下包含提醒信息,可以在JSON數據中添加一個特定的字段,例如 error_message ,來指示錯誤信息。在前端中,可以檢查返回的JSON數據是否包含該字段,如果包含,則可以將其顯示為提醒信息。
如果前端在請求中添加了 responseType: ‘blob’ 參數,但是后端返回的數據是JSON格式的,那么前端需要將響應體轉換為JSON格式的數據類型,才能對其進行處理。以下是一種可能的解決方案:
axios.get(‘/api/download’, {
responseType: ‘blob’
}).then(response => {
if (response.data.type === ‘application/json’) {
// 如果響應類型為JSON格式,則將響應體轉換為JSON格式的數據類型
return response.json();
} else {
// 如果響應類型為其他類型,則直接返回響應體
return response.data;
}
}).then(data => {
// 在這里對JSON數據進行處理
}).catch(error => {
// 處理錯誤
});
在這個例子中,我們首先使用 axios 庫發起一個帶有 responseType: ‘blob’ 參數的GET請求。當響應返回時,我們檢查響應的類型是否為JSON格式。如果是,我們將響應體轉換為JSON格式的數據類型。否則,我們直接返回響應體。
接著,我們使用 then 方法對返回的數據進行處理。如果響應類型為JSON格式,我們可以直接使用返回的JSON數據進行處理。否則,我們需要將響應體轉換為JSON格式的數據類型,然后再進行處理。
需要注意的是,在使用 response.json() 方法將響應體轉換為JSON格式的數據類型時,需要確保響應體的類型確實為JSON格式。如果響應體的類型不是JSON格式,這個方法將會拋出一個異常。
不應該判斷后端返回數據的響應頭content-type嗎?
是的,判斷后端返回數據的響應頭content-type是更好的做法。因為content-type是HTTP響應頭中標識響應體類型的字段,它能夠準確地告訴前端響應體的類型,從而避免了前端對響應體類型的猜測和判斷。
以下是一種使用content-type判斷響應體類型的解決方案:
axios.get(‘/api/download’, {
responseType: ‘blob’
}).then(response => {
const contentType = response.headers[‘content-type’];
if (contentType === ‘application/json’) {
// 如果響應類型為JSON格式,則將響應體轉換為JSON格式的數據類型
return response.json();
} else {
// 如果響應類型為其他類型,則直接返回響應體
return response.data;
}
}).then(data => {
// 在這里對JSON數據進行處理
}).catch(error => {
// 處理錯誤
});
在這個例子中,我們首先使用 axios 庫發起一個帶有 responseType: ‘blob’ 參數的GET請求。當響應返回時,我們獲取響應頭中的content-type字段,并根據其值判斷響應體的類型。如果響應類型為JSON格式,我們將響應體轉換為JSON格式的數據類型。否則,我們直接返回響應體。
接著,我們使用 then 方法對返回的數據進行處理。如果響應類型為JSON格式,我們可以直接使用返回的JSON數據進行處理。否則,我們需要將響應體轉換為JSON格式的數據類型,然后再進行處理。
需要注意的是,在使用 response.json() 方法將響應體轉換為JSON格式的數據類型時,需要確保響應體的類型確實為JSON格式。如果響應體的類型不是JSON格式,這個方法將會拋出一個異常。
前端工具
nvm
簡介
NodeVersionManager(NVM)在處理多項目中不同Node.js版本需求的解決方案。NVM允許輕松切換版本,包括下載遠程LTS版本、Windows安裝步驟、配置淘寶鏡像、基本使用命令及常見操作。對于Node.js開發者來說,NVM簡化了版本管理,提高工作效率。
下載配置
配置源
C:\Users\qctc\AppData\Roaming\nvm\settings.txt
添加
node_mirror: http://npmmirror.com/mirrors/node/
npm_mirror: http://registry.npmmirror.com/mirrors/npm/
使用
--查看版本
nvm -v
--看安裝的所有node.js的版本
nvm ls
--查顯示可以安裝的所有node.js的版本
nvm list available
--安裝
nvm install 16.15.0
--卸載對應node版本(如:nvm uninstall 17.2.0)
nvm uninstall 版本號
--使用某個版本
nvm use 16.15.0
Vite
簡介
Vite 是一個現代化的前端構建工具,旨在提高開發和構建速度。它由 Evan You(Vue.js 的創始人)開發,旨在解決傳統構建工具(如 Webpack)在開發和構建過程中的一些痛點。以下是對 Vite 的詳細介紹,包括其特性、工作原理、優點以及如何使用它。
特性
- 極速啟動和熱重載:
- Vite 利用原生 ES 模塊(ESM)來實現開發時的快速啟動和即時熱重載(HMR)。在開發過程中,Vite 不需要進行打包,而是直接利用瀏覽器對 ES 模塊的原生支持,動態地加載模塊。
- 按需編譯:
- 在開發時,Vite 會按需編譯文件,只編譯那些被實際引用的模塊。這種按需編譯的方式大大減少了編譯的時間和計算資源。
- 優化構建:
- Vite 使用 Rollup 作為生產環境的構建工具,這提供了高效的靜態資源打包、代碼分割和 Tree-shaking 功能。Rollup 是一個成熟的打包工具,以其高效的打包性能而聞名。
- 兼容性:
- Vite 支持現代瀏覽器的原生 ES 模塊功能,但也提供了降級機制,使得可以通過插件進行老舊瀏覽器的兼容性處理。
- 插件系統:
- Vite 擁有一個豐富的插件生態系統,支持擴展和自定義功能。插件系統與 Rollup 的插件系統兼容,可以復用許多現有的 Rollup 插件。
- 內置開發服務器:
- Vite 自帶一個開發服務器,可以提供熱重載、錯誤提示等功能,提升開發體驗。
工作原理
- 開發模式:
- 在開發模式下,Vite 使用原生 ES 模塊直接在瀏覽器中加載源代碼。這意味著 Vite 可以實現極快的啟動速度和即時的模塊熱重載。
- 當模塊被修改時,Vite 只重新編譯變化的模塊,并通過 WebSocket 發送更新到瀏覽器,瀏覽器接收到更新后會刷新修改的模塊。
- 生產模式:
- 在生產模式下,Vite 使用 Rollup 進行項目的打包。Rollup 對 JavaScript 進行高級優化,包括代碼分割、Tree-shaking 等,以生成高效的生產構建文件。
- Vite 配置 Rollup 用于構建優化,因此用戶可以使用 Rollup 插件和配置進行進一步的優化。
優點
- 快速啟動:
- 由于開發時無需進行打包,Vite 的啟動速度遠快于傳統工具,尤其是大型項目。
- 即刻反饋:
- Vite 的熱重載非常快速,修改文件后可以即時看到效果,大大提高了開發效率。
- 現代化工具鏈:
- Vite 支持現代 JavaScript 特性,如 ES 模塊,TypeScript,CSS 模塊等,提供了更為現代化的開發體驗。
- 配置簡單:
- Vite 的配置非常簡單易用,默認配置已經適用于大多數常見的使用場景。如果需要自定義,配置也很直觀。
- 支持多種前端框架:
- Vite 原生支持 Vue、React、Svelte 等常見前端框架,并提供了框架特定的插件來簡化集成。
使用
安裝
可以使用 npm 或 yarn 安裝 Vite。以下是創建一個新項目的步驟:
# 使用 npm
npm create vite@latest my-project# 使用 yarn
yarn create vite my-project
選擇你想要的框架和模板,Vite 將自動生成一個基礎項目結構。
啟動
進入項目目錄后,使用以下命令啟動開發服務器:
cd my-project
npm install
npm run dev
或者使用 yarn:
cd my-project
yarn install
yarn dev
這將啟動一個本地開發服務器,你可以在瀏覽器中訪問 http://localhost:3000
查看項目。
構建
當你完成開發并準備好構建生產版本時,可以使用以下命令:
npm run build
或者使用 yarn:
yarn build
構建后的文件會生成在 dist
目錄中,可以用來部署到生產環境。
前端基礎
CSS
三大特性
- 層疊性:如果發生了樣式沖突,那就會根據一定的規則(選擇器優先級),進行樣式層疊(覆蓋)。
- 繼承性:元素會自動擁有其父元素,或其祖先元素上所設置的某些樣式。優先繼承離得近的
- 優先級:!important > 行內樣式 > ID選擇器 > 類選擇器 > 元素選擇器 > * >繼承的樣式(多個選擇器需要計算權重)
display屬性
CSS中的display屬性用于定義元素的布局方式和外觀,讓塊級元素和行內元素相互轉換。常見的取值有block、inline、inline-block、none等
- none:讓生成的元素根本沒有框,該框及其內容不再顯示,不占用文檔中的空間
- block塊級:讓行內元素(比如元素)表現得像塊級元素一樣,設置width、height有效,獨占一行
- inline行內:讓塊級元素(比如
元素)表現得像內聯或行內元素一樣,設置width、height無效,一行可顯示多個
- inline-block行內塊:設置width、height有效,一行可顯示多個
盒子模型
盒子模型是指CSS中的一個重要概念,用來描述一個元素在頁面中占據的空間和位置
在CSS中認為所有的HTML標簽都是一個盒子,這些盒子有以下內容:邊框border、內容content、內邊距padding(內容與邊框的距離)、外邊距margin(盒子與盒子之間的距離)
- 盒子有尺寸,用CSS寫法為寬度(width)和高度(height)
- 盒子有邊框,用CSS寫法為上下左右邊框(border)
- 盒子有內邊距,CSS寫法為上下左右內邊距(padding)
- 盒子有外邊距,CSS寫法為上下左右外邊距(margin)
Padding 和 Margin
padding 屬性用于設置元素內容與其邊框之間的間距(內邊距)
padding 屬性可以通過以下幾種方式進行設置:
- 單個值:應用于所有四個邊。
- 兩個值:第一個值應用于上下邊,第二個值應用于左右邊。
- 三個值:第一個值應用于上邊,第二個值應用于左右邊,第三個值應用于下邊。
- 四個值:依次應用于上、右、下、左邊。
margin
屬性用于設置元素與其相鄰元素之間的間距(外邊距)
margin
屬性的設置方式與 padding
類似
Flex彈性布局
Flex是Flexible Box的縮寫,意為”彈性布局”,用來替代float浮動布局
采用Flex布局的元素,稱為Flex容器(flex container),簡稱”容器”。它的所有子元素自動成為容器成員,稱為Flex項目(flex item),簡稱”項目”。
容器默認存在兩根軸:水平的主軸(main axis)和垂直的交叉軸(cross axis)。
主軸的開始位置(與邊框的交叉點)叫做main start,結束位置叫做main end;交叉軸的開始位置叫做cross start,結束位置叫做cross end。
項目默認沿主軸排列。單個項目占據的主軸空間叫做main size,占據的交叉軸空間叫做cross size。
flex-direction屬性
flex-direction屬性決定主軸的方向(即項目的排列方向)。
- row(默認值):主軸為水平方向,起點在左端。
- row-reverse:主軸為水平方向,起點在右端。
- column:主軸為垂直方向,起點在上沿。
- column-reverse:主軸為垂直方向,起點在下沿。
flex-wrap屬性
默認情況下,項目都排在一條線(又稱”軸線”)上。flex-wrap屬性定義,如果一條軸線排不下,如何換行。
nowrap(默認):不換行。
wrap:換行,第一行在上方。
wrap-reverse:換行,第一行在下方。
justify-content
- 定義:
justify-content
控制的是主軸(flexbox 的默認主軸是水平方向,grid 的主軸依賴于布局方向)的對子元素的對齊方式。 - 屬性值:
flex-start
:子元素靠近主軸的開始位置。flex-end
:子元素靠近主軸的結束位置。center
:子元素在主軸上居中對齊。space-between
:子元素在主軸上均勻分布,首尾元素靠邊。space-around
:子元素在主軸上均勻分布,每個元素兩側都有相等的空間。
align-items
- 定義:
align-items
控制的是交叉軸(flexbox 的默認交叉軸是垂直方向)上的對齊方式。 - 屬性值:
flex-start
:子元素在交叉軸的開始位置對齊。flex-end
:子元素在交叉軸的結束位置對齊。center
:子元素在交叉軸上居中對齊。baseline
:子元素的基線對齊。stretch
:子元素被拉伸以填充容器(這是默認值)。
align-content屬性
align-content屬性定義了多根軸線的對齊方式。如果項目只有一根軸線,該屬性不起作用。也就是垂直對齊,不分行對齊。
- flex-start:與交叉軸的起點對齊。
- flex-end:與交叉軸的終點對齊。
- center:與交叉軸的中點對齊。
- space-between:與交叉軸兩端對齊,軸線之間的間隔平均分布。
- space-around:每根軸線兩側的間隔都相等。所以,軸線之間的間隔比軸線與邊框的間隔大一倍。
- stretch(默認值):軸線占滿整個交叉軸。
邊框
基本用法
border: [border-width] [border-style] [border-color];
border: 2px solid red;
JavaScript/TypeScript
javaScript常用數組方法
順序 | 方法名 | 功能 | 返回值 | 是否改變原數組 | 版本 |
---|---|---|---|---|---|
1 | push() | (在結尾)向數組添加一或多個元素 | 返回新數組長度 | Y | ES5 |
2 | unshift() | (在開頭)向數組添加一或多個元素 | 返回新數組長度 | Y | ES5 |
3 | find() | 遍歷數組,執行回調函數,回調函數執行一個條件,返回滿足條件的第一個元素,不存在返回undefined | 滿足條件第一個元素/否則返回undefined | N | ES6 |
4 | forEach() | (迭代) 遍歷數組,每次循環中執行傳入的回調函數 | 無/(undefined) | N | ES5- |
5 | splice() | 向數組中添加,或從數組刪除,或替換數組中的元素,然后返回被刪除/替換的元素所組成的數組。可以實現數組的增刪改 | 被刪除/替換的元素所組成的數組 | ||
6 | map() | 遍歷數組, 每次循環時執行傳入的回調函數,根據回調函數的返回值,生成一個新的數組 | |||
7 | join() | 用特定的字符,將數組拼接形成字符串 (默認",") | 返回拼接后的字符串 | N | ES5- |
async/await
async
和 await
是 JavaScript 中用于處理異步操作的語法糖,它們使得編寫異步代碼更加簡潔和可讀。以下是它們的詳細解釋和用法。
1. 基本概念
async
:用于聲明一個異步函數,該函數返回一個 Promise 對象。即使你在函數中沒有顯式返回 Promise,函數會自動封裝為 Promise。await
:用于等待一個 Promise 的解析。它只能在async
函數內部使用。await
表達式會暫停異步函數的執行,直到 Promise 完成并返回結果。
2. 使用方法
2.1 聲明異步函數
使用 async
關鍵字聲明一個異步函數:
async function myAsyncFunction() {// 這里的代碼是異步的
}
2.2 使用 await
在異步函數中,你可以使用 await
來等待 Promise 的結果:
async function fetchData() {const response = await axios.get('/api/data'); // 等待請求完成const data = response.data; // 獲取數據return data; // 返回數據
}
3. 完整示例
下面是一個完整的示例,展示如何使用 async
和 await
:
import axios from 'axios';async function getUserData(userId) {try {const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`);console.log('用戶數據:', response.data);} catch (error) {console.error('請求錯誤:', error);}
}getUserData(1); // 調用函數,獲取用戶數據
4. 錯誤處理
使用 try...catch
語句可以有效地處理異步操作中的錯誤:
async function fetchData() {try {const response = await axios.get('/api/data');console.log('數據:', response.data);} catch (error) {console.error('請求失敗:', error);}
}
5. 關鍵點總結
- 提高可讀性:使用
async/await
使得異步代碼更接近于同步代碼的書寫風格,增強可讀性。 - 錯誤處理:可以使用
try...catch
來捕獲錯誤,處理方式更為簡潔。 - 不阻塞主線程:盡管代碼看起來是順序執行的,
await
不會阻塞主線程,仍然可以執行其他操作。
6. 限制
await
只能在async
函數內部使用。- 如果你在非
async
函數中使用await
,會拋出語法錯誤。
7. 結論
async/await
是處理 JavaScript 異步操作的強大工具,使得異步代碼更易于編寫、閱讀和維護。通過結合 Promise 和 async/await
,可以有效管理異步請求和其他異步操作。
Promise 對象
Promise 是一種用于表示異步操作的最終完成(或失敗)及其結果值的對象。它有三種狀態:
- Pending(待定):初始狀態,既不是成功,也不是失敗。
- Fulfilled(已完成):操作成功完成。
- Rejected(已拒絕):操作失敗。
使用 Promise
可以更好地管理異步操作,使得代碼更清晰。
async/await
是基于 Promise
的語法糖,用于簡化異步代碼的編寫。通過將函數聲明為 async
,你可以在函數內部使用 await
等待一個 Promise 的解析,從而使異步代碼更接近同步的寫法。
Axios 是一個基于 Promise 的 HTTP 客戶端,用于發送網絡請求。當你使用 Axios 進行請求時,它返回一個 Promise。你可以用 .then()
和 .catch()
方法處理結果,或在 async
函數中使用 await
來獲取數據。
示例
import axios from 'axios';async function fetchData() {try {const response = await axios.get('/api/data'); // axios 返回一個 Promiseconsole.log('數據:', response.data);} catch (error) {console.error('請求錯誤:', error);}
}fetchData();
總結
- Promise 處理異步操作的結果。
- async/await 提供了更易讀的語法來處理 Promise。
- Axios 使用 Promise 進行網絡請求,能與
async/await
無縫結合,提高代碼可讀性。
定時執行 setInterval方法
setInterval
是 JavaScript 中用于定時執行函數的一個全局方法。它的主要作用是在指定的時間間隔內重復調用一個函數或執行一段代碼。這在需要周期性地執行某些任務時非常有用,比如更新界面、輪播圖的切換、定時檢查狀態等。
語法
let intervalID = setInterval(function, delay, arg1, arg2, ...);
- function:要執行的函數,可以是一個函數引用或匿名函數。
- delay:延遲的時間(以毫秒為單位),即每次調用之間的間隔時間。
- arg1, arg2, …:可選的參數,在調用函數時傳給該函數。
返回值
setInterval
返回一個整數 ID,這個 ID 可以用于停止定時器。這個 ID 是唯一的標識符,用于后續通過clearInterval
方法來清除該定時器。
示例
簡單示例
下面是一個簡單的例子,使用 setInterval
每隔一秒打印一次當前時間:
let intervalID = setInterval(() => {let date = new Date();console.log("當前時間: " + date.toLocaleTimeString());
}, 1000);
停止定時器
可以使用 clearInterval
來停止定時器,如下所示:
// 停止定時器
clearInterval(intervalID);
可以調用 clearInterval(intervalID)
來停止之前設置的 setInterval
,如在一定條件下停止定時執行。
js-cookie庫
js-cookie
是一個簡單易用的 JavaScript 庫,用于操作 Cookie。以下是其基本用法:
-
設置 Cookie:
Cookies.set('key', 'value'); // 可選參數:{ expires: 7, path: '/', domain: '.example.com' }
-
獲取 Cookie:
const value = Cookies.get('key');
-
刪除 Cookie:
Cookies.remove('key'); // 保存7天 const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); Cookies.set('key', '', { expires });
-
讀取所有 Cookie:
const allCookies = Cookies.get();
使用 js-cookie
可簡化 Cookie 的操作,便于在前端存儲和讀取數據。
斷言
變量非空斷言(Non-null Assertion)是一種特殊的類型斷言,用于告訴編譯器某個變量或表達式不是 null
或 undefined
。這種斷言通常用后綴 !
表示。
為什么需要非空斷言
在 TypeScript 中,很多類型默認可能是 null
或 undefined
。例如,在接口中未賦值的可選屬性,默認值就是 undefined
。非空斷言可以幫助你在某些情況下避免冗長的類型檢查。
非空斷言的用法
非空斷言的基本語法是在變量或表達式后面加上 !
數字格式化
使用padStart(length, '0')
函數,length表示長度(位數),'0’表示位數不足時補充0,通常結合toString()和parseInt(t1[0])使用
let length = 2;
let t1 = row.allowedCharging.split("_");
startTime1.value = parseInt(t1[0]).toString().padStart(length, '0')+":00"
endTime1.value = parseInt(t1[1]).toString().padStart(length, '0')+":00"
let t2 = row.allowedDischarge.split("_")
startTime2.value = parseInt(t2[0]).toString().padStart(length, '0')+":00"
endTime2.value = parseInt(t2[1]).toString().padStart(length, '0')+":00"
讀取解析Excel
使用原生的input組件,結合xlsx.js解析Excel文件,解析結果為數組,每行也是數組
<template><el-dialog :title="fileDialogTitle" v-model="openfile" width="700px" append-to-body><el-row style="display: flex; justify-content: center; padding: 30px auto;"><el-col :span="12"><input type="file" ref="fileInput" accept=".xlsx,.xls" /></el-col></el-row><template #footer><div class="dialog-footer"><el-button type="primary" @click="submitFile">確 定</el-button><el-button @click="cancel">取 消</el-button></div></template></el-dialog>
</template>
<script setup lang="ts">
const openfile = ref<boolean>(false);
const fileDialogTitle = ref<string>("打開文件");
const fileInput = ref<HTMLInputElement | null>(null);function handleFileChange() {const file = fileInput.value?.files?.[0];if (!file) { // 檢查是否有文件選擇ElMessage.info('請選擇一個 Excel 文件!');} else {console.log('選擇的文件:', file.name); // 輸出選擇的文件}
};async function submitFile() {const file = fileInput.value?.files?.[0];if (!file) {ElMessage.info('請選擇一個 Excel 文件!');return;}let tableHeaders: any = ['fl', 'lx', 'm1', '', '', '', '', '', '', ''];const reader = new FileReader();reader.onload = (e) => {const data = new Uint8Array(e.target?.result as ArrayBuffer);const workbook = XLSX.read(data, { type: 'array' });// 讀取第一個工作表const worksheet = workbook.Sheets[workbook.SheetNames[0]];const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });console.log(jsonData)// 設置表頭和數據if (jsonData.length > 0) {jsonData.forEach((value: unknown, index: number, array: unknown[]) => {console.log(value + " " + index + " ")if (Array.isArray(value)) {if (index >= 2) {tableData[index - 2].m1 = value[2];tableData[index - 2].m2 = value[3];tableData[index - 2].m3 = value[4];tableData[index - 2].m4 = value[5];tableData[index - 2].m5 = value[6];tableData[index - 2].m6 = value[7];tableData[index - 2].m7 = value[8];tableData[index - 2].m8 = value[9];tableData[index - 2].m9 = value[10];tableData[index - 2].m10 = value[11];tableData[index - 2].m11 = value[12];tableData[index - 2].m12 = value[13];}}})openfile.value = false;}};reader.readAsArrayBuffer(file);
};
function cancel() {open.value = falseopenfile.value = false
}
</script>
Element-Plus框架
Container布局容器
用于布局的容器組件,方便跨蘇搭建頁面的基本結構
<el-container>
:外層容器。 當子元素中包含<el-header>
或<el-footer>
時,全部子元素會垂直上下排列, 否則會水平左右排列。<el-header>
:頂欄容器。<el-aside>
:側邊欄容器。<el-main>
:主要區域容器。<el-footer>
:底欄容器。
以上組件采用了 flex 布局,使用前請確定目標瀏覽器是否兼容。 此外,
<el-container>
的直接子元素必須是后四個組件中的一個或多個。 后四個組件的父元素必須是一個<el-container>
Color色彩
Element Plus 為了避免視覺傳達差異,使用一套特定的調色板來規定顏色,為你所搭建的產品提供一致的外觀視覺感受。
參考頁面:色彩
Layout布局
通過基礎的24分欄,迅速簡便地創建布局
使用列創建基礎網格布局。
通過 row
和 col
組件,并通過 col 組件的 span
屬性我們就可以自由地組合布局。
行提供 gutter
屬性來指定列之間的間距,其默認值為0。
通過制定 col 組件的 offset
屬性可以指定分欄偏移的欄數。
您可以通過justify
屬性來定義子元素的排版方式,其取值為start、center、end、space-between、space-around或space-evenly。
row的屬性
屬性名 | 說明 | 類型 | 默認值 |
---|---|---|---|
gutter | 柵格間隔 | number | 0 |
justify | flex 布局下的水平排列方式 | enum | start |
align | flex 布局下的垂直排列方式 | enum | — |
tag | 自定義元素標簽 | string | div |
justify:‘start’ | ‘end’ | ‘center’ | ‘space-around’ | ‘space-between’ | ‘space-evenly’
align:‘top’ | ‘middle’ | ‘bottom’
行提供 gutter
屬性來指定列之間的間距,其默認值為0。
<template><el-row :gutter="20"><el-col :span="6"><div class="grid-content ep-bg-purple" /></el-col><el-col :span="6"><div class="grid-content ep-bg-purple" /></el-col><el-col :span="6"><div class="grid-content ep-bg-purple" /></el-col><el-col :span="6"><div class="grid-content ep-bg-purple" /></el-col></el-row>
</template>
col的屬性
屬性名 | 說明 | 類型 | 默認值 |
---|---|---|---|
span | 柵格占據的列數 | number | 24 |
offset | 柵格左側的間隔格數 | number | 0 |
push | 柵格向右移動格數 | number | 0 |
pull | 柵格向左移動格數 | number | 0 |
xs | <768px 響應式柵格數或者柵格屬性對象 | number / object | — |
sm | ≥768px 響應式柵格數或者柵格屬性對象 | number / object | — |
md | ≥992px 響應式柵格數或者柵格屬性對象 | number / object | — |
lg | ≥1200px 響應式柵格數或者柵格屬性對象 | number / object | — |
xl | ≥1920px 響應式柵格數或者柵格屬性對象 | number / object | — |
tag | 自定義元素標簽 | string | div |
Button 按鈕
基礎用法
<template><div class="mb-4"><el-button>Default</el-button><el-button type="primary">Primary</el-button><el-button type="success">Success</el-button><el-button type="info">Info</el-button><el-button type="warning">Warning</el-button><el-button type="danger">Danger</el-button></div><div class="mb-4"><el-button plain>Plain</el-button><el-button type="primary" plain>Primary</el-button><el-button type="success" plain>Success</el-button><el-button type="info" plain>Info</el-button><el-button type="warning" plain>Warning</el-button><el-button type="danger" plain>Danger</el-button></div><div class="mb-4"><el-button round>Round</el-button><el-button type="primary" round>Primary</el-button><el-button type="success" round>Success</el-button><el-button type="info" round>Info</el-button><el-button type="warning" round>Warning</el-button><el-button type="danger" round>Danger</el-button></div><div><el-button :icon="Search" circle /><el-button type="primary" :icon="Edit" circle /><el-button type="success" :icon="Check" circle /><el-button type="info" :icon="Message" circle /><el-button type="warning" :icon="Star" circle /><el-button type="danger" :icon="Delete" circle /></div><!-- 常用 -->
<el-button type="primary" :icon="Plus" plain @click="handleAdd">添加</el-button>
<el-button type="warning" :icon="Delete" plain @click="handleDel">刪除</el-button>
<el-button type="success" :icon="Select" plain @click="handleSave">保存</el-button>
<el-button type="primary" :icon="DataAnalysis" plain>統計信息</el-button>
<el-button type="primary" :icon="DataAnalysis" plain>分月信息</el-button>
<el-button type="primary" :icon="Plus" plain>添加電廠</el-button>
<el-button type="primary" :icon="Upload" plain>導入</el-button>
<el-button type="primary" :icon="Download" plain>導出</el-button>
</template><script lang="ts" setup>
import {Check,Delete,Edit,Message,Search,Star,
} from '@element-plus/icons-vue'
</script><!-- 禁用 -->
<el-button type="primary" disabled>Primary</el-button>
<!-- 加載中 -->
<el-button type="primary" :loading="execbtn" :icon="Select" @click="execinit">執行初始化</el-button>
Table 表格
列內容居中
<el-table-column align="center" prop="swjd" label="上網節點" />
列內容不顯示
對el-table-column
使用v-if="false"
<el-table-column property="unitid" label="機組ID" width="100" v-if="false" />
列內容格式化
給formatter屬性添加處理函數
<el-table-column property="isFixedCurve" label="是否固定功率曲線" align="center" :formatter="isOrNotFormatter" />
<script setup lang="ts">const isOrNot = reactive([{ label: "是", value: '1' }, { label: '否', value: '0' }])function isOrNotFormatter(row: any, column: any, cellValue: any, index: number){return isOrNot.find(e => e.value === cellValue)?.label || '未知';}
</script>
固定列
固定列需要使用 fixed
屬性,它接受 Boolean
值。
斑馬紋
stripe
可以創建帶斑馬紋的表格
行樣式
row-style屬性
<el-table :data="tableData" style="width: 1680px; height: 400px; max-height: 400px;" stripe border :row-style="rowStyle"></el-table>
<script lang='ts'>const rowStyle = ({ row, rowIndex }: { row: any, rowIndex: number }) => {// 根據行索引或行數據動態設置樣式if (rowIndex % 2 === 0) {return { backgroundColor: '#f0f0f0', opacity: 0.8 };} else {return { backgroundColor: '#ffffff', opacity: 1 };}};
</script>
帶邊框表格
默認情況下,Table 組件是不具有豎直方向的邊框的, 如果需要,可以使用 border
屬性
顯示溢出提示
屬性 show-overflow-tooltip 接受一個布爾值。 為 true
時多余的內容會在 hover 時以 tooltip 的形式顯示出來。
流體高度
通過設置 max-height
屬性為 el-table
指定最大高度。 此時若表格所需的高度大于最大高度,則會顯示一個滾動條。
單選
選擇單行數據時使用色塊表示。
Table 組件提供了單選的支持, 只需要配置 highlight-current-row
屬性即可實現單選。 之后由 current-change
事件來管理選中時觸發的事件,它會傳入 currentRow
,oldCurrentRow
。 如果需要顯示索引,可以增加一列 el-table-column
,設置 type
屬性為 index
即可顯示從 1 開始的索引號。
<template><el-tableref="singleTableRef":data="tableData"highlight-current-rowstyle="width: 100%"@current-change="handleCurrentChange"><el-table-column type="index" width="50" /><el-table-column property="date" label="Date" width="120" /><el-table-column property="name" label="Name" width="120" /><el-table-column property="address" label="Address" /></el-table>
</template>
<script lang="ts" setup>const currentRow = ref()const handleCurrentChange = (val: User | undefined) => {currentRow.value = val}
</script>
多選
在2.8.3 之后, toggleRowSelection
支持第三個參數 ignoreSelectable
以確定是否忽略可選屬性。
實現多選非常簡單: 手動添加一個 el-table-column
,設 type
屬性為 selection
即可
<template><el-tableref="multipleTableRef":data="tableData"style="width: 100%"@selection-change="handleSelectionChange"><el-table-column type="selection" :selectable="selectable" width="55" /><el-table-column label="Date" width="120"><template #default="scope">{{ scope.row.date }}</template></el-table-column><el-table-column property="name" label="Name" width="120" /><el-table-column property="address" label="Address" /></el-table><div style="margin-top: 20px"><el-button @click="toggleSelection([tableData[1], tableData[2]])">Toggle selection status of second and third rows</el-button><el-button @click="toggleSelection([tableData[1], tableData[2]], false)">Toggle selection status based on selectable</el-button><el-button @click="toggleSelection()">Clear selection</el-button></div>
</template><script lang="ts" setup>
import { ref } from 'vue'
import type { TableInstance } from 'element-plus'interface User {id: numberdate: stringname: stringaddress: string
}const multipleTableRef = ref<TableInstance>()
const multipleSelection = ref<User[]>([])const selectable = (row: User) => ![1, 2].includes(row.id)
const toggleSelection = (rows?: User[], ignoreSelectable?: boolean) => {if (rows) {rows.forEach((row) => {multipleTableRef.value!.toggleRowSelection(row,undefined,ignoreSelectable)})} else {multipleTableRef.value!.clearSelection()}
}
const handleSelectionChange = (val: User[]) => {multipleSelection.value = val
}......
表格布局
通過屬性 table-layout 可以指定表格中單元格、行和列的布局方式
<template><el-radio-group v-model="tableLayout"><el-radio-button value="fixed">fixed</el-radio-button><el-radio-button value="auto">auto</el-radio-button></el-radio-group><el-table :data="tableData" :table-layout="tableLayout"><el-table-column prop="date" label="Date" /><el-table-column prop="name" label="Name" /><el-table-column prop="address" label="Address" /></el-table>
</template><script lang="ts" setup>
import { ref } from 'vue'
import type { TableInstance } from 'element-plus'const tableLayout = ref<TableInstance['tableLayout']>('fixed')
...
單元格合并
多行或多列共用一個數據時,可以合并行或列。
通過給 table 傳入span-method
方法可以實現合并行或列, 方法的參數是一個對象,里面包含當前行 row
、當前列 column
、當前行號 rowIndex
、當前列號 columnIndex
四個屬性。 該函數可以返回一個包含兩個元素的數組,第一個元素代表 rowspan
,第二個元素代表 colspan
。 也可以返回一個鍵名為 rowspan
和 colspan
的對象。
<template><div><el-table:data="tableData":span-method="arraySpanMethod"borderstyle="width: 100%"><el-table-column prop="id" label="ID" width="180" /><el-table-column prop="name" label="Name" /><el-table-column prop="amount1" sortable label="Amount 1" /><el-table-column prop="amount2" sortable label="Amount 2" /><el-table-column prop="amount3" sortable label="Amount 3" /></el-table><el-table:data="tableData":span-method="objectSpanMethod"borderstyle="width: 100%; margin-top: 20px"><el-table-column prop="id" label="ID" width="180" /><el-table-column prop="name" label="Name" /><el-table-column prop="amount1" label="Amount 1" /><el-table-column prop="amount2" label="Amount 2" /><el-table-column prop="amount3" label="Amount 3" /></el-table></div>
</template>
<script lang="ts" setup>
import type { TableColumnCtx } from 'element-plus'interface User {id: stringname: stringamount1: stringamount2: stringamount3: number
}interface SpanMethodProps {row: Usercolumn: TableColumnCtx<User>rowIndex: numbercolumnIndex: number
}const arraySpanMethod = ({row,column,rowIndex,columnIndex,
}: SpanMethodProps) => {if (rowIndex % 2 === 0) {if (columnIndex === 0) {return [1, 2]} else if (columnIndex === 1) {return [0, 0]}}
}const objectSpanMethod = ({row,column,rowIndex,columnIndex,
}: SpanMethodProps) => {if (columnIndex === 0) {if (rowIndex % 2 === 0) {return {rowspan: 2,colspan: 1,}} else {return {rowspan: 0,colspan: 0,}}}
}const tableData: User[] = [{id: '12987122',name: 'Tom',amount1: '234',amount2: '3.2',amount3: 10,},{id: '12987123',name: 'Tom',amount1: '165',amount2: '4.43',amount3: 12,},{id: '12987124',name: 'Tom',amount1: '324',amount2: '1.9',amount3: 9,},{id: '12987125',name: 'Tom',amount1: '621',amount2: '2.2',amount3: 17,},{id: '12987126',name: 'Tom',amount1: '539',amount2: '4.1',amount3: 15,},
]
</script>
刪除某行
function handleDel(){console.log("點擊了刪除按鈕")if (!currentRow.value) {ElMessage.warning("請選擇要刪除的數據")return;}// console.log(currentRow.value);tableData.value.splice(tableData.value.indexOf(currentRow.value), 1);ElMessage.success("刪除成功!");
}
某列搜索
<el-table ref="mxTableRef" :data="filteredData"style="width: 100%; height: 730px; max-height: 730px;" stripe border:row-style="rowStyle" highlight-current-row @current-change="handleCurrentChange"><el-table-column prop="name" label="母線名稱" align="center"><template #header>
<span>母線名稱</span>
<el-input v-model="mxSearch" placeholder="搜索母線" style="width: 120px"/></template></el-table-column><el-table-column prop="limit" label="最大限制(MW)" width="120" align="center" /><el-table-column prop="avg" label="平均負載率(%)" width="120" align="center" /><el-table-column prop="max" label="最大負載率(%)" width="120" align="center" />
</el-table>
<script>
const filteredData = computed(() => {return tableData.value.filter(item =>item.name.includes(mxSearch.value));
});
</script>
填充測試數據
tableData.value = [];const intervalMinutes = 15; // 每15分鐘一個時間點const startDate = new Date();startDate.setHours(0, 0, 0, 0); // 設置為當天的00:00for (let i = 0; i < 96; i++) {const currentTime = new Date(startDate.getTime() + i * intervalMinutes * 60 * 1000);const hours = currentTime.getHours().toString().padStart(2, '0');const minutes = currentTime.getMinutes().toString().padStart(2, '0');const sk = `${hours}:${minutes}`;// 生成隨機的sjfh和ycfh值,可以根據實際需求調整const sjfh = (Math.random() * 100).toFixed(2);const ycfh = (Math.random() * 100).toFixed(2);tableData.value.push({sk: sk,gl: parseFloat(sjfh),fbyz: 1,llxxe: parseFloat(ycfh)});}
雙擊單元格修改數據
<el-table ref="table1Ref" :data="tableData1"style="width: 100%; height: 100%; max-height: 100%;" stripe border :row-style="rowStyle"@cell-dblclick="cellDbclick1" highlight-current-row><!-- 場景ID 場景名稱 場景類型 負荷變動系數 風電變動系數 光伏變動系數 送出變動系數 受入變動系數 是否生效 --><el-table-column prop="sceneId" label="場景ID" align="center" /><el-table-column prop="sceneName" label="場景名稱" align="center" /><el-table-column prop="sceneType" label="場景類型" align="center" /><el-table-column v-for="(column, index) in columns1" :key="index" :prop="column.prop":label="column.label" align="center"><template #default="{ row }">
<div v-if="editing && editing.row === row && editing.prop === column.prop"><el-input :ref="`input-${row.sceneId}-${column.prop}`" v-model="row[column.prop]"@blur="handleInputBlur($event, row, column.prop)"@keydown="handleInputKeydown($event, row, column.prop)"></el-input></div>
<div v-else>{{ row[column.prop] }}</div></template></el-table-column>
</el-table>
function cellDbclick1(row: any, column: any, cell: HTMLTableCellElement, event: Event) {console.log(column)editing.value = { row, prop: column.property };// nextTick(() => {// const inputRef = document.querySelector(`input-${row.sceneId}-${column.property}`);// // const inputRef = table1Ref.value?.getRef(`input-${row.sceneId}-${column.property}`)// console.log(inputRef);// // if (inputRef) {// // inputRef.focus();// // }// });}
function handleInputBlur(event, row, prop) {console.log(row, prop);row[prop] = event.target.value;updData1(row);this.editing = null;
}
function handleInputKeydown(event, row, prop) {if (event.key === 'Enter') {row[prop] = event.target.value;updData1(row);this.editing = null;}
}
右鍵菜單
主要是使用了el-table組件的cell-contextmenu事件屬性
<template>
<el-tableref="table1Ref":data="tableData1"style="width: 100%; height: 100%; max-height: 100%;"stripeborder:row-style="rowStyle"@cell-dblclick="cellDbclick1"@cell-contextmenu="handleCellContextMenu"highlight-current-row></el-table>
<script setup lang="ts">
function cellContextmenu(row: any, column: any, cell: HTMLTableCellElement, event: MouseEvent) {event.preventDefault(); // 阻止默認的右鍵菜單// console.log(row)// console.log(column)const menuItems = initstates.value.map(item => ({label: item.name,icon: 'el-icon-edit',action: () => {// 處理單元格右鍵菜單點擊事件row.prestate = item.id;}}));const contextMenu = document.createElement('div');contextMenu.style.position = 'absolute';contextMenu.style.left = `${event.clientX}px`;contextMenu.style.top = `${event.clientY}px`;contextMenu.style.backgroundColor = '#fff';contextMenu.style.border = '1px solid #ccc';contextMenu.style.boxShadow = '2px 2px 5px rgba(0, 0, 0, 0.3)';contextMenu.style.zIndex = '1000';menuItems.forEach(item => {const menuItem = document.createElement('div');menuItem.className = 'context-menu-item';menuItem.innerHTML = `<i class="${item.icon}"></i> ${item.label}`;menuItem.addEventListener('click', () => {item.action();contextMenu.remove();});contextMenu.appendChild(menuItem);});document.body.appendChild(contextMenu);// 添加點擊空白區域關閉菜單的事件const closeMenu = (e: MouseEvent) => {if (!contextMenu.contains(e.target as Node)) {contextMenu.remove();document.removeEventListener('click', closeMenu);}};document.addEventListener('click', closeMenu);
}
</script>
列內容改為按鈕
<!-- 修改按鈕 -->
<el-table-column fixed="right" label="操作" width="100"><template #default="scope"><el-button type="primary" size="small" @click="handleUpdate(scope.row)">修改</el-button></template>
</el-table-column>
<script setup lang="ts">function handleUpdate(row: any) {form.value = row;openUpdate.value = true;}
</script>
分頁
<el-row style="display: flex; justify-content: center; margin-top: 5px"><el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[20, 96, 200, 1000]" :size="size" :disabled="disabled" :background="background" layout="sizes, prev, pager, next" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</el-row>
const currentPage = ref(1)
const pageSize = ref(20)
const size = ref<ComponentSize>('default')
const background = ref(false)
const disabled = ref(false)
const total = ref<number>(1000)const handleSizeChange = (val: number) => {console.log(`${val} items per page`)initTable()
}
const handleCurrentChange = (val: number) => {console.log(`current page: ${val}`)initTable()
}
Dialog對話框
<el-dialog :title="addOrUpdateTitle" v-model="open" width="700px" append-to-body>
</el-dialog>
例子
<el-dialog :title="addOrUpdateTitle" v-model="openAddOrUpdate" width="700px" append-to-body><el-row><!-- 斷面名稱 包含設備數 正極限 負極限 開始日期 結束日期 數據來源 狀態 --><el-form :model="form" :rules="rules" ref="userRef" label-width="90px"><el-form-item label="序號"><el-input type="number" v-model="form!.xh" /></el-form-item><el-form-item label="停電設備名稱"><el-input v-model="form!.tdsbmc" placeholder="停電設備名稱" /></el-form-item><el-form-item label="檢修開始日期"><el-date-picker v-model="form!.jxksrq" type="date" placeholder="檢修開始日期" style="width: 100%"format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item><el-form-item label="檢修結束日期"><el-date-picker v-model="form!.jxjsrq" type="date" placeholder="檢修結束日期" style="width: 100%"format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item><el-form-item label="檢修天數"><el-input v-model="form!.jxts" disabled /></el-form-item><el-form-item label="設別類型"><el-select v-model="form!.sblx" placeholder="請選擇設別類型"><el-option v-for="jxlx in sblxs" :label="jxlx.label" :value="jxlx.value" /></el-select></el-form-item><el-form-item label="電壓等級"><el-select v-model="form!.dydj" placeholder="請選擇電壓等級"><el-option v-for="jxlx in dydjs" :label="jxlx.label" :value="jxlx.value" /></el-select></el-form-item><el-form-item label="檢修內容"><el-input type="texg" v-model="form!.jxnr" /></el-form-item></el-form></el-row><template #footer><div class="dialog-footer"><el-button type="primary" @click="okBtn">確 定</el-button><el-button @click="closeBtn">取 消</el-button></div></template></el-dialog>
<script>
const openAddOrUpdate = ref<boolean>(false);
const addOrUpdateTitle = ref<string>("新增");
const form = ref<tableRow>()
const zts = ref<any[]>([{ label: "正常", value: "正常" }, { label: "異常", value: "異常" }])function okBtn() {console.log("點擊了確定按鈕")console.log(form.value)if (addOrUpdateTitle.value === "新增") {if (form.value){tableData.value.push(form.value!)}} else if (addOrUpdateTitle.value === "更新") {let index = tableData.value.indexOf(selectionRep.value[0])tableData[index] = form.value}form.value = { ...form.value, bhsbs: 0, dmmc: "", zjx: 0, fjx: 0, ksrq: "", jsrq: "", sjly: "", zt: "" }openAddOrUpdate.value = false;ElMessage.success(addOrUpdateTitle.value + '成功!請點擊保存按鈕進行保存');
}
function closeBtn() {console.log("點擊了取消按鈕")console.log(form)openAddOrUpdate.value = false;
}
</script>
對話框中展示其它組件
<el-dialog title="發電計劃正式優化(SCUC)" v-model="openDialog2" append-to-body class="sfdy-dialog-style":show-close="true" :close-on-click-modal="false" :transition="'fade'" destroy-on-close><keep-alive><component :is="kt3CurrentComponent" /></keep-alive><template #footer><divstyle="width: 100%;display: flex; justify-content: center; align-items: center; gap: 20px;"><el-button type="primary" @click="kt3CurrentComponent = Kt3Tab1Content">概覽</el-button><el-button type="primary" :icon="Histogram" plain@click="kt3CurrentComponent = Kt3Tab2Content">新能源消納情況</el-button><el-button type="primary" :icon="Histogram" plain@click="kt3CurrentComponent = Kt3Tab3Content">系統備用情況</el-button><el-button type="primary" :icon="Histogram" plain@click="kt3CurrentComponent = Kt3Tab4Content">機組發電計劃</el-button><el-button type="primary" :icon="Histogram" plain@click="kt3CurrentComponent = Kt3Tab5Content">電網潮流情況</el-button></div></template></el-dialog>
相關變量
// 課題三部分
import Kt3Tab1Content from '../jgzs/component/kt3Overview.vue';
import Kt3Tab2Content from '../jgzs/xnyxn.vue';
import Kt3Tab3Content from '../jgzs/dmclfx.vue';
import Kt3Tab4Content from '../jhbz/jzjhjg.vue';
import Kt3Tab5Content from '../jgzs/dwclqk.vue';const kt3CurrentComponent = ref(Kt3Tab1Content);
Form表單
<el-form :model="form" :rules="rules" ref="userRef" label-width="90px"><el-form-item label="斷面名稱" prop="dmmc"><el-input v-model="formModel.dmmc" placeholder="斷面名稱" /></el-form-item><el-form-item label="包含設備數" v-show="false" prop="bhsbs"><el-input v-model="formModel!.bhsbs" placeholder="包含設備數" disabled /></el-form-item><el-form-item label="正極限" prop="zjx"><el-input type="number" v-model="formModel!.zjx" /></el-form-item><el-form-item label="負極限" prop="fjx"><el-input type="number" v-model="formModel!.fjx" /></el-form-item><el-form-item label="開始日期" prop="ksrq"><el-date-picker v-model="formModel!.ksrq" type="date" placeholder="" style="width: 180px" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item><el-form-item label="結束日期" prop="jsrq"><el-date-picker v-model="formModel!.jsrq" type="date" placeholder="" style="width: 180px" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item><el-form-item label="數據來源" prop="sjly"><el-input v-model="formModel!.sjly" placeholder="" /></el-form-item><el-form-item label="狀態" prop="zt"><el-select v-model="formModel!.zt" placeholder="請選擇狀態"><el-option v-for="jxlx in zts" :label="jxlx.label" :value="jxlx.value" /></el-select></el-form-item><el-form-item><el-button type="primary" @click="okBtn(formRef)">確 定</el-button><el-button @click="closeBtn">取 消</el-button></el-form-item>
</el-form>
表單驗證
屬性::rules=“rules”
<script lang="ts" setup>
const rules = reactive<FormRules<RuleForm>>({name: [{ required: true, message: '請輸入斷面名稱', trigger: 'blur' },{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },],region: [{required: true,message: 'Please select Activity zone',trigger: 'change',},],
})
</script>
普通輸入框
一鍵清空屬性 clearable
<template><el-inputv-model="input"style="width: 240px"placeholder="Please input"clearable/>
</template><script lang="ts" setup>
import { ref } from 'vue'
const input = ref('')
</script>
文本域
文本域高度可通過 rows
屬性控制
<template><el-inputv-model="textarea"style="width: 240px":rows="2"type="textarea"placeholder="Please input"/>
</template><script lang="ts" setup>
import { ref } from 'vue'
const textarea = ref('')
</script>
使用input-style修改組件
<el-input v-model="ycwtlbText" style="width: 100%;height: 250px" type="textarea"input-style="background-color: transparent;height: 100%" disabled />
日期選擇
<!-- 普通日期 -->
<el-date-picker v-model="clsxxDate" type="date" align="center" format="YYYY-MM-DD" value-format="YYYY-MM-DD"/>
<!-- 選擇月 -->
<el-date-picker v-model="value5" type="month" placeholder="Pick a month" format="YYYY-MM" value-format="YYYY-MM" />
數字輸入框
<!-- min和max設置最大最小值 step設置步進值 precision設置精度 -->
<el-input-number v-model="fdnum" :min="0" :max="1" :step="0.01" :precision="2"/>
<!-- 帶前綴 --><el-input-number v-model="num" :min="1" :max="10"><template #prefix><span>¥</span></template></el-input-number>
<!-- 帶后綴,有問題,不生效 --><el-input-number v-model="num" :min="1" :max="10"><template #suffix><span>RMB</span></template></el-input-number>
<!-- 建議這種 -->
<el-input type="number" v-model="yssx" style="width: 120px;"><template #suffix> <span>MW</span> </template>
</el-input>
下拉選擇框
<el-select v-model="value" value-key="id" placeholder="Select" style="width: 240px"><el-option v-for="item in options" :key="item.id" :label="item.label" :value="item"/>
</el-select>
const value = ref<any>()
注意 value-key=“id”
分割線
普通橫向
<template><div><!-- 普通使用(橫向) --><el-divider /><!-- 左側添加字 --><el-divider content-position="left">Rabindranath Tagore</el-divider><!-- 中間添加圖標 --><el-divider><el-icon><star-filled /></el-icon></el-divider><!-- 右側添加字 --><el-divider content-position="right">Rabindranath Tagore</el-divider><!-- 虛線1 --><el-divider border-style="dashed" /><!-- 虛線2 --><el-divider border-style="dotted" /><!-- 虛線3 --><el-divider border-style="double" /><!-- 垂直 --><el-divider direction="vertical" /></div>
</template>
Tree樹
帶搜索框的樹
<template><el-inputv-model="filterText"style="width: 240px"placeholder="Filter keyword"/><el-treeref="treeRef"style="max-width: 600px"class="filter-tree":data="data":props="defaultProps"default-expand-all:filter-node-method="filterNode"highlight-current/>
</template><script lang="ts" setup>
import { ref, watch } from 'vue'
import { ElTree } from 'element-plus'interface Tree {[key: string]: any
}const filterText = ref('')
const treeRef = ref<InstanceType<typeof ElTree>>()const defaultProps = {children: 'children',label: 'label',
}watch(filterText, (val) => {treeRef.value!.filter(val)
})const filterNode = (value: string, data: Tree) => {if (!value) return truereturn data.label.includes(value)
}const data: Tree[] = [{id: 1,label: 'Level one 1',children: [{id: 4,label: 'Level two 1-1',children: [{id: 9,label: 'Level three 1-1-1',},{id: 10,label: 'Level three 1-1-2',},],},],},{id: 2,label: 'Level one 2',children: [{id: 5,label: 'Level two 2-1',},{id: 6,label: 'Level two 2-2',},],},{id: 3,label: 'Level one 3',children: [{id: 7,label: 'Level two 3-1',},{id: 8,label: 'Level two 3-2',},],},
]
</script>
高亮選擇節點
添加屬性 highlight-current
節點選擇事件
@current-change=“changeLlxSelect”
<el-tree ref="treeRef" style="max-width: 100%" class="filter-tree" :data="treeData":props="defaultProps" default-expand-all :filter-node-method="filterNode"@current-change="changeLlxSelect" highlight-current />
<script>
function changeLlxSelect(val: any, obj: any) {if (obj.isLeaf) {console.log(val)console.log(obj)initSelectedLlx();}
}
</script>
默認選擇
el-tree
組件使用 default-checked-keys
屬性來設置默認選中的節點。
卡片
<!-- 陰影效果 --><el-card style="width: 480px" shadow="always">Always</el-card><el-card style="width: 480px" shadow="hover">Hover</el-card><el-card style="width: 480px" shadow="never">Never</el-card>
<!-- 帶標題和頁腳 -->
<el-card style="max-width: 480px"><template #header><div class="card-header"><span>Card name</span></div></template><p v-for="o in 4" :key="o" class="text item">{{ 'List item ' + o }}</p><template #footer>Footer content</template>
</el-card>
<!-- 設置內容樣式 -->
<el-card style="" body-style="padding: 10px 20px !important;">
</el-card>
標簽頁
<template><el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick"><el-tab-pane label="User" name="first">User</el-tab-pane><el-tab-pane label="Config" name="second">Config</el-tab-pane><el-tab-pane label="Role" name="third">Role</el-tab-pane><el-tab-pane label="Task" name="fourth">Task</el-tab-pane></el-tabs>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import type { TabsPaneContext } from 'element-plus'const activeName = ref('first')const handleClick = (tab: TabsPaneContext, event: Event) => {console.log(tab, event)
}
</script><style>
.demo-tabs > .el-tabs__content {padding: 32px;color: #6b778c;font-size: 32px;font-weight: 600;
}
</style>
提示確認框
ElMessageBox.confirm('確定進行計劃日期為 ' + selectedDate.value + "、計劃方案為 " + selectedplan.value + '的計劃編制嗎?').then(() => {console.log("開始計算")
}).catch(() => {// catch error
})
時間線
可視化地呈現時間流信息。
<template><el-timeline style="max-width: 600px"><el-timeline-itemv-for="(activity, index) in activities":key="index":icon="activity.icon":type="activity.type":color="activity.color":size="activity.size":hollow="activity.hollow":timestamp="activity.timestamp">{{ activity.content }}</el-timeline-item></el-timeline>
</template><script lang="ts" setup>
import { MoreFilled } from '@element-plus/icons-vue'const activities = [{content: 'Custom icon',timestamp: '2018-04-12 20:46',size: 'large',type: 'primary',icon: MoreFilled,},{content: 'Custom color',timestamp: '2018-04-03 20:46',color: '#0bbd87',},{content: 'Custom size',timestamp: '2018-04-03 20:46',size: 'large',},{content: 'Custom hollow',timestamp: '2018-04-03 20:46',type: 'primary',hollow: true,},{content: 'Default node',timestamp: '2018-04-03 20:46',},
]
</script>
Echarts
在vue3中使用
const myChart1 = shallowRef<echarts.ECharts | null>(null);if (!myChart1.value){myChart1.value = echarts.init(document.getElementById('kt2Chart1') as HTMLDivElement);}myChart1.value.setOption(option1);// 掛載
onMounted(() => {
// 其它操作nextTick(() => {initKt2Chart();});
});// 卸載時銷毀
onUnmounted(() => {myChart1 && myChart1.value.dispose();myChart1.value = null;
});
折線圖
import * as echarts from 'echarts';type EChartsOption = echarts.EChartsOption;var chartDom = document.getElementById('main')!;
var myChart = echarts.init(chartDom);
var option: EChartsOption;option = {title: {text: 'Main Title',subtext: 'Sub Title',left: 'center',// top: "center",textStyle: {fontSize: 30},subtextStyle: {fontSize: 20}},xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [150, 230, 224, 218, 135, 147, 260],type: 'line'}]
};option && myChart.setOption(option);
重復刷新圖形,加上true
option && myChart.setOption(option, true);
甘特圖
import * as echarts from 'echarts';type EChartsOption = echarts.EChartsOption;var chartDom = document.getElementById('main')!;
var myChart = echarts.init(chartDom);
var option: EChartsOption;option = {tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'},formatter: function (params: any) {let date = new Date('2024-01-01');date.setDate(date.getDate() + params.data);return ('開始日期:' +date.toISOString().split('T')[0] +'<br />結束日期:' +date.toISOString().split('T')[0]);}// formatter: '開始日期: {c0}<br />結束日期: {c1}'},grid: {left: '3%',right: '4%',bottom: '3%',containLabel: true},yAxis: [{type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']}],xAxis: [{type: 'value',max: 365,min: 0,axisLabel: {formatter: function (value, index) {let date = new Date('2024-01-01');date.setDate(date.getDate() + value);return date.toISOString().split('T')[0];}}}],series: [{name: '開始',type: 'bar',stack: 'Ad',itemStyle: {opacity: 0},data: [(new Date('2024-10-05').getTime() - new Date('2024-01-01').getTime()) /(24 * 60 * 60 * 1000),100,100,100,100,100,100]},{name: '檢修',type: 'bar',stack: 'Ad',emphasis: {focus: 'series'},data: [new Date('2024-10-15').getDate() - new Date('2024-10-05').getDate(),100,100,100,100,100,100]}]
};option && myChart.setOption(option);
流程圖
function initChart() {const option = {tooltip: {},// legend: {// data: [// { name: '已完成', icon: 'circle', itemStyle: { color: '#67C23A' } },// { name: '執行中', icon: 'circle', itemStyle: { color: '#E6A23C' } },// { name: '錯誤', icon: 'circle', itemStyle: { color: '#F56C6C' } },// { name: '未執行', icon: 'circle', itemStyle: { color: '#909399' } }// ],// selectedMode: false, // 禁用圖例的點擊交互// top: 20,// left: 'center',// textStyle: {// fontSize: 12// }// },series: [{type: 'graph',layout: 'none',symbolSize: 50,// roam: true,roam: false,label: {show: true},edgeSymbol: ['circle', 'arrow'],edgeSymbolSize: [4, 10],edgeLabel: {fontSize: 20},data: nodes.value.map(node => ({id: node.id,name: node.name,x: node.x,y: node.y,itemStyle: {color: statusColors[node.status]},symbol: node.symbol,label: {show: true,position: 'bottom',distance: 10, // 標簽與節點的距離color: '#666',fontSize: 14,fontWeight: 'bold'}})),links: edges.value.map(edge => ({source: edge.source,target: edge.target,lineStyle: {color: '#aaa'}})),}, {type: 'graph',layout: 'none',symbolSize: 50,// roam: true,roam: false,label: {show: true},edgeSymbol: ['circle', 'arrow'],edgeSymbolSize: [4, 10],edgeLabel: {fontSize: 20},z: 1,data: nodesBg.value.map(node => ({id: node.id,name: node.name,x: node.x,y: node.y,itemStyle: {color: statusColors[node.status]},symbol: node.symbol,label: {show: false,}})),},]};processChart.setOption(option);processChart.off('click');processChart.on('click', params => {if (params.componentType === 'series' && params.seriesType === 'graph') {if (params.dataType === 'node') {// console.log('點擊了節點:', params.data);const nodeData = params.data as { id: string; name: string }; // 類型斷言if (nodeData.id === 'checked2') {showKt3();} else if (nodeData.id === 'checked3') {showKt2();} else if (nodeData.id === 'checked4') {showKt1();}}}});
}
節點和邊
// 動態更新
watch(checkList, () => {initChart();
}, { deep: true })const nodesBg = computed(() => {const nodesTmp = [];nodesTmp.push({id: 'start', name: '', x: 150, y: 180, z: 3, status: '未執行',symbol: `image://${defaultIcon}`,symbolSize: [45, 45],});if (checkList.value.length === 0) {nodesTmp.push({id: 'end', name: '', x: 700, y: 180, status: '未執行', symbol: `image://${defaultIcon}`,symbolSize: [45, 45],});return nodesTmp;}let index = 0;if (checkList.value.indexOf("checked3") > -1) {index++;// 基于預機組組合和前瞻調度的儲能調峰計劃優化算法(課題二)nodesTmp.push({id: 'checked3', name: "", x: 150 + index * 150, y: 180, status: '未執行', symbol: `image://${defaultIcon}`,symbolSize: [45, 45],})}if (checkList.value.indexOf("checked2") > -1) {index++;// 省網非市場機組日前出力計劃的自動編排(課題三)nodesTmp.push({id: 'checked2', name: "", x: 150 + index * 150, y: 180, status: '未執行', symbol: `image://${defaultIcon}`,symbolSize: [45, 45],})}if (checkList.value.indexOf("checked4") > -1) {index++;// 日前多時段交流最優潮流技術(課題一)nodesTmp.push({id: 'checked4', name: "", x: 150 + index * 150, y: 180, status: '未執行', symbol: `image://${defaultIcon}`,symbolSize: [45, 45],})}index++;nodesTmp.push({id: 'end', name: '結果發布', x: 150 + index * 150, y: 180, status: '未執行', symbol: `image://${defaultIcon}`,symbolSize: [45, 45],});return nodesTmp;
})
const nodes = computed(() => {const nodesTmp = [];nodesTmp.push({id: 'start', name: '數據準備', x: 150, y: 180, z: 2, status: '未執行',symbol: `image://${icon1}`,symbolSize: [40, 40],});if (checkList.value.length === 0) {nodesTmp.push({id: 'end', name: '結果發布', x: 700, y: 180, status: '未執行',symbol: `image://${icon5}`,symbolSize: [40, 40],});return nodesTmp;}let index = 0;if (checkList.value.indexOf("checked3") > -1) {index++;// 基于預機組組合和前瞻調度的儲能調峰計劃優化算法(課題二)nodesTmp.push({id: 'checked3', name: "儲能預優化", x: 150 + index * 150, y: 180, status: '未執行',symbol: `image://${icon2}`,symbolSize: [40, 40],})}if (checkList.value.indexOf("checked2") > -1) {index++;// 省網非市場機組日前出力計劃的自動編排(課題三)nodesTmp.push({id: 'checked2', name: "發電計劃正式優化(SCUC)", x: 150 + index * 150, y: 180, status: '未執行',symbol: `image://${icon3}`,symbolSize: [40, 40],})}if (checkList.value.indexOf("checked4") > -1) {index++;// 日前多時段交流最優潮流技術(課題一)nodesTmp.push({id: 'checked4', name: "發電計劃精細化修正(OPF)", x: 150 + index * 150, y: 180, status: '未執行',symbol: `image://${icon4}`,symbolSize: [40, 40],})}index++;nodesTmp.push({id: 'end', name: '結果發布', x: 150 + index * 150, y: 180, status: '未執行',symbol: `image://${icon5}`,symbolSize: [40, 40],});return nodesTmp;
})const edges = computed(() => {const edges = [];if (checkList.value.indexOf("checked3") > -1) {edges.push({ source: 'start', target: 'checked3' });}if (checkList.value.indexOf("checked2") > -1) {edges.push({ source: edges.length === 0 ? 'start' : edges[edges.length - 1].target, target: 'checked2' });}if (checkList.value.indexOf("checked4") > -1) {edges.push({ source: edges.length === 0 ? 'start' : edges[edges.length - 1].target, target: 'checked4' });}edges.push({ source: edges.length === 0 ? 'start' : edges[edges.length - 1].target, target: 'end' });return edges;
})
引入圖片,使用// @ts-ignore抑制警告
// @ts-ignore
import defaultIcon from '@/assets/images/process/default.png';
Vue3
生命周期函數
- beforeCreate
- 組件實例剛被創建,數據觀測和事件配置尚未進行。
- 不可訪問
data
、computed
、methods
和生命周期中的this
。
- created
- 組件實例已被創建,數據觀測和事件配置已完成。
- 這時可以訪問
data
、computed
、methods
和生命周期中的this
,但 DOM 尚未被掛載。
- beforeMount
- 在掛載開始之前被調用,此時模板已編譯到虛擬 DOM 但尚未插入到 DOM 中。
- mounted
- 組件掛載完成后調用,此時可以訪問到真實的 DOM 元素。
- 常用于進行數據請求或其他需要訪問 DOM 的操作。
- beforeUpdate
- 在數據變化時,組件重新渲染之前調用。
- 常用于做一些操作,比如在更新之前保存數據的狀態。
- updated
- 組件重新渲染后調用。
- 此時可以根據更新后的狀態進行進一步的操作。
- beforeUnmount
- 組件卸載之前調用,此時可以做一些清理工作,比如取消定時器或解綁事件。
- unmounted
- 組件卸載后調用。
- 此時可以確認組件已被完全卸載。
- activated (keep-alive 組件中)
- 當組件被激活時調用。
- 適用于使用
<keep-alive>
包裹的組件。
- deactivated (keep-alive 組件中)
- 當組件被停用時調用。
- 適用于使用
<keep-alive>
包裹的組件。
ref函數
是Vue3中的一個函數,一般用于將基本類型數據處理成響應式數據
作用:定義一個基本類型的響應式數據,只有數據成為響應式數據,這樣當數據變化時,才能被檢測到
使用時需要從vue中引入
<script>import { ref } from "vue"; // 引入響應式函數ref...
</script>
語法:const xxx = ref(數據變量)
結果:返回一個RefImpl的引用對象,獲取時為xxx.value
在頁面模板使用數據直接使用插值表達式,不需要加value,例如 姓名:{{ name }}
接受的數據可以是基本類型,此時的原理是給數據設置get和set監聽函數
也可以是對象類型,此時的原理是Proxy
reactive函數
定義一個對象類型的響應式數據,返回一個Proxy實例對象,不能用于基本類型
語法:const 代理對象 = reactive(源對象)
接收一個對象或數組,返回一個代理對象
使用時先從vue中引入
reactive和ref的對比
- 數據定義角度:
- ref用來定義基本數據類型,也可以定義對象或數組,但內容還是通過reactive轉換為代理對象
- reactive用來定義對象或數組
- 原理角度:
- ref通過get和set監聽函數實現數據的響應式,是數據劫持
- reactive通過代理實現數據響應式的,并通過Reflect來操作源對象數據
- 使用角度:
- ref定義的數據操作時需要使用value,而插值表達式中不需要value
- reactive定義的數據都不需要value
setup語法糖
是Vue3引入的新特性之一,是組合式API的核心部分,旨在簡化組件邏輯的組織和管理
setup
函數概述
setup
函數是 Vue 3 組件的一個新的生命周期鉤子,它在組件創建之前執行,并且是在創建組件實例之后、模板編譯之前調用的。它主要用于設置組件的響應式狀態、計算屬性和方法。
基本用法
html<template><div><p>Message: {{ message }}</p><button @click="increment">Increment</button><p>Count: {{ count }}</p></div>
</template><script>
import { ref, computed } from 'vue';export default {setup() {// 響應式狀態const count = ref(0);// 計算屬性const message = computed(() => `The count is ${count.value}`);// 方法const increment = () => {count.value++;};// 返回模板需要的數據和方法return {count,message,increment};}
}
</script>
setup
函數的特點
- 響應式狀態:
- 使用 Vue 3 提供的響應式 API,如
ref
和reactive
,在setup
函數中定義組件的響應式狀態。 ref
用于定義基本類型的響應式數據,而reactive
用于定義復雜類型的響應式數據。
- 使用 Vue 3 提供的響應式 API,如
- 計算屬性:
- 使用
computed
函數定義計算屬性,類似于 Vue 2 中的計算屬性。
- 使用
- 方法:
- 在
setup
函數中定義的方法可以直接在模板中使用。
- 在
- 返回值:
setup
函數需要返回一個對象,該對象的屬性和方法將暴露給模板(<template>
)和其他組件的上下文。
- 生命周期鉤子:
- 通過
onMounted
、onUpdated
、onUnmounted
等 API 在setup
中使用生命周期鉤子。
- 通過
相比 Options API 的優勢
- 邏輯復用:
setup
允許通過組合函數(composable functions)來復用邏輯,使組件邏輯更加模塊化和可重用。
- 類型推斷:
- 更好地支持 TypeScript 的類型推斷和類型檢查。
- 更清晰的組織:
- 使組件的邏輯更加清晰和集中,尤其是對于大型組件或復雜邏輯。
例子:組合函數
可以創建一個組合函數來封裝和復用邏輯:
javascript// useCounter.js
import { ref, computed } from 'vue';export function useCounter() {const count = ref(0);const increment = () => count.value++;const message = computed(() => `The count is ${count.value}`);return {count,message,increment};
}
然后在組件中使用這個組合函數:
<template><div><p>Message: {{ message }}</p><button @click="increment">Increment</button><p>Count: {{ count }}</p></div>
</template><script>
import { useCounter } from './useCounter';export default {setup() {const { count, message, increment } = useCounter();return {count,message,increment};}
}
</script>
總結
setup
函數是 Vue 3 中 Composition API 的核心特性,它提供了一個新的方式來組織組件的邏輯,使代碼更加模塊化和清晰。通過在 setup
中定義響應式狀態、計算屬性和方法,并將它們返回給模板,可以更好地管理和復用組件邏輯。
計算屬性 computed properties
在 Vue 3 中,計算屬性(computed properties)是一種基于響應式數據進行計算的屬性。它們類似于 Vue 2 中的計算屬性,但在 Vue 3 的 Composition API 中也有對應的實現。
計算屬性的主要特點是它們的值是基于依賴的響應式數據自動計算和緩存的,只在相關依賴發生變化時才會重新計算。
計算屬性的特點
- 基于依賴進行計算: 計算屬性的值是基于其他響應式數據自動計算的,當這些數據發生變化時,計算屬性會重新計算其值。
- 緩存: 計算屬性會緩存其計算結果,只有當其依賴的數據發生變化時才會重新計算。這使得計算屬性比直接在模板中寫表達式更高效,避免了不必要的重復計算。
- 簡化模板: 計算屬性可以將復雜的邏輯提取到 JavaScript 代碼中,使模板代碼更簡潔和易讀。
如何使用計算屬性
在 Vue 3 中,計算屬性是通過 computed
函數來創建的。下面是一個簡單的例子:
<template><div><p>Full Name: {{ fullName }}</p><input v-model="firstName" placeholder="First Name" /><input v-model="lastName" placeholder="Last Name" /></div>
</template><script>
import { ref, computed } from 'vue';export default {setup() {const firstName = ref('');const lastName = ref('');// 定義計算屬性const fullName = computed(() => {return `${firstName.value} ${lastName.value}`;});return {firstName,lastName,fullName};}
}
</script>
在這個例子中,fullName
是一個計算屬性,它會根據 firstName
和 lastName
的值自動計算和更新。
計算屬性的用處
- 數據變換: 計算屬性可以用來格式化或變換數據。例如,將日期格式化為特定格式,或計算某個值的總和。
- 條件計算: 可以根據多個響應式數據的值計算一個復雜的條件,并在模板中直接使用計算結果。
- 提高性能: 由于計算屬性是基于依賴緩存的,所以它們在依賴的數據沒有變化時不會重新計算,避免了不必要的重復計算,從而提高性能。
計算屬性和網絡請求
計算屬性不適合用于執行網絡請求。網絡請求通常需要在組件的生命周期中進行,并且可能會有副作用,比如修改數據或處理異步操作。通常,你會在 setup
函數中使用 Vue 3 的 watch
、watchEffect
或生命周期鉤子(如 onMounted
)來進行網絡請求。
例如,如果你要在頁面加載時進行網絡請求,可以使用 onMounted
鉤子:
import { ref, onMounted } from 'vue';
import axios from 'axios';export default {setup() {const data = ref(null);const error = ref(null);onMounted(async () => {try {const response = await axios.get('https://api.example.com/data');data.value = response.data;} catch (err) {error.value = err;}});return {data,error};}
}
在這個例子中,onMounted
鉤子用于在組件加載完成后執行網絡請求。計算屬性則不適合這種場景,因為它們不支持異步操作或副作用。
總結
- 計算屬性:在 Vue 3 中,計算屬性通過
computed
函數創建,用于基于響應式數據進行高效的計算和緩存。它們適用于將復雜邏輯從模板中分離出來,并且在依賴的數據沒有變化時不會重復計算。 - 網絡請求:網絡請求應在組件的生命周期鉤子(如
onMounted
)或watch
、watchEffect
中處理,不適合在計算屬性中執行,因為計算屬性不支持異步操作。
scoped,在style標簽中
vue為了防止css樣式污染,在每個組件中提供了 scoped
屬性進行限定css作用域;
當 <style>
標簽有 scoped 屬性時,它的 CSS 只作用于當前組件中的元素。
scope,在template標簽中
在 Vue 中,<template>
標簽內的 #default="scope"
是一個作用域插槽的語法。這里的 scope
是一個對象,包含了當前行的數據和其他一些有用的信息。具體來說:
scope.row
:當前行的數據對象。scope.$index
:當前行的索引。scope.column
:當前列的對象。scope.store
:表格的 store 對象,包含了一些內部狀態和方法。
在你的代碼中,scope.row
代表了當前行的數據對象,你可以通過它訪問該行的所有字段。例如,handleUpdate(scope.row)
方法會被調用,并且傳遞當前行的數據作為參數。
總結一下,scope
是一個包含當前行數據和其他信息的對象,通過它可以方便地獲取和操作表格中的數據。
emit
在 Vue 3 中,emit
是一個用于子組件向父組件傳遞數據的方法。
通過 emit
,子組件可以觸發自定義事件,并將數據傳遞給父組件。
以下是 emit
的詳細用法:
-
聲明自定義事件
在子組件中,可以通過
emits
選項來聲明可以觸發的自定義事件。這有助于提高代碼的可讀性和維護性。
<template> <button @click="handleClick">點擊我</button>
</template>
<script> export default { emits: ['customEvent'], // 聲明可以觸發的自定義事件 methods: { handleClick() { this.$emit('customEvent', '這是傳遞的數據'); // 觸發自定義事件并傳遞數據 } } }
</script>
2.使用 setup
函數 在使用 setup
函數的組合式 API 中,可以通過 context.emit
來觸發自定義事件。
<template> <button @click="handleClick">點擊我</button>
</template>
<script> import { defineComponent } from 'vue'; export default defineComponent({ emits: ['customEvent'], // 聲明可以觸發的自定義事件 setup(props, context) { const handleClick = () => { context.emit('customEvent', '這是傳遞的數據'); // 觸發自定義事件并傳遞數據 }; return { handleClick }; } });
</script>
- 在父組件中監聽自定義事件 父組件可以通過
v-on
指令或@
簡寫形式來監聽子組件觸發的自定義事件。
<template> <ChildComponent @customEvent="handleCustomEvent" />
</template>
<script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, methods: { handleCustomEvent(data) { console.log(data); // 輸出: 這是傳遞的數據 } } }
</script>
- 動態綁定事件 可以在父組件中使用動態綁定來監聽子組件的自定義事件。
<template> <ChildComponent v-on="listeners" />
</template>
<script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, data() { return { listeners: { customEvent: this.handleCustomEvent } }; }, methods: { handleCustomEvent(data) { console.log(data); // 輸出: 這是傳遞的數據 } } }
</script>
- 使用修飾符 Vue 3 支持在
v-on
指令上使用修飾符,如.once
和.native
。
.once
:事件只觸發一次。
.native
:監聽組件根元素的原生事件。
<template><ChildComponent @customEvent.once="handleCustomEvent" />
</template>
<script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, methods: { handleCustomEvent(data) { console.log(data); // 輸出: 這是傳遞的數據 } } }
</script>
總結
emit
用于子組件向父組件傳遞數據。- 可以通過
emits
選項聲明可以觸發的自定義事件。 - 在
setup
函數中使用context.emit
觸發自定義事件。 - 父組件通過
v-on
或@
監聽子組件的自定義事件。 - 支持動態綁定和修飾符的使用。
watch
在 Vue 3 中,watch
是一個響應式偵聽器,用于監聽數據的變化。當被監聽的數據變化時,watch
會執行相應的回調函數。它通常用于處理副作用操作,如 API 請求或操作 DOM。
使用方法:
watch
主要有兩種使用方式:
- 監聽單個響應式數據或計算屬性:
import { ref, watch } from 'vue';const count = ref(0);watch(count, (newVal, oldVal) => {console.log(`count changed from ${oldVal} to ${newVal}`);
});count.value = 1; // 輸出: count changed from 0 to 1
- 監聽多個數據或計算屬性:
import { ref, computed, watch } from 'vue';const a = ref(1);
const b = ref(2);
const sum = computed(() => a.value + b.value);watch([a, b], ([newA, newB], [oldA, oldB]) => {console.log(`a changed from ${oldA} to ${newA}`);console.log(`b changed from ${oldB} to ${newB}`);
});
watch
的參數:
-
第一個參數:可以是一個響應式數據、計算屬性或數組(監聽多個數據)。
-
第二個參數:回調函數,接收
newVal
(新值)和oldVal
(舊值)作為參數。 -
可選的第三個參數
:配置選項,常見的選項有:
immediate: true
:在偵聽開始時立即執行一次回調。deep: true
:深度偵聽嵌套對象或數組的變化。
示例:立即執行和深度監聽
import { ref, watch } from 'vue';const user = ref({ name: 'John', age: 30 });watch(user, (newVal, oldVal) => {console.log('User changed:', newVal);
}, { deep: true, immediate: true });
總結:
watch
用于響應式數據變化時執行副作用操作。- 可以監聽單個或多個數據。
- 支持深度監聽、立即執行等配置選項。
SCSS
SCSS(Sassy CSS)是一種CSS預處理器,它是Sass(Syntactically Awesome Style Sheets)語法的一種擴展。
語法
使用大括號和分號,語法類似于CSS。例如:
.container {color: blue;.button {background-color: red;}
}
CSS:遵循標準的CSS語法,沒有嵌套和變量等特性。
嵌套
-
SCSS:支持嵌套規則,使得樣式層次更加清晰。
-
CSS:沒有嵌套的能力,必須逐層定義選擇器。
變量
-
SCSS
:可以定義變量,方便管理顏色、字體等重復使用的值。
scss$primary-color: blue; .header {color: $primary-color; }
-
CSS:原生CSS也支持變量(CSS自定義屬性),但在SCSS中使用更為廣泛。
混入(Mixins)
-
SCSS:支持混入,可以定義一組樣式,并在多個選擇器中復用。
scss@mixin border-radius($radius) {border-radius: $radius; }.box {@include border-radius(10px); }
-
CSS:沒有混入的概念。
運算
-
SCSS
:支持數學運算,可以直接在樣式中進行加減乘除。
scss.box {width: 100px + 20px; }
-
CSS:原生CSS不支持這樣的運算。
導入
- SCSS:可以使用
@import
導入其他SCSS文件,并自動處理依賴。 - CSS:雖然也可以用
@import
導入,但在性能和管理上不如SCSS高效。
總結
SCSS提供了許多增強功能,使得樣式表的編寫和管理更加高效和模塊化。它在大型項目中尤其有用,因為它可以提高代碼的可讀性和可維護性。最終,SCSS會被編譯成標準的CSS,以供瀏覽器使用。
Restful風格
在 Java 開發中,前后端分離時,RESTful 風格是一種設計 Web 服務的架構風格,它基于 HTTP 協議,利用 HTTP 方法(GET、POST、PUT、DELETE 等)來操作資源。以下是 RESTful 風格的簡要講解:
資源(Resources)
資源 是 RESTful 設計的核心概念,通常對應于數據庫中的表或業務實體。
資源通過 URI(Uniform Resource Identifier)進行標識,例如 /users
表示用戶資源。
HTTP 方法
- GET:用于獲取資源,例如
GET /users
獲取所有用戶。 - POST:用于創建資源,例如
POST /users
創建一個新用戶。 - PUT:用于更新資源,例如
PUT /users/1
更新 ID 為 1 的用戶。 - DELETE:用于刪除資源,例如
DELETE /users/1
刪除 ID 為 1 的用戶。 - PATCH:用于部分更新資源,例如
PATCH /users/1
更新 ID 為 1 的用戶的某個屬性
狀態碼
- 200 OK:請求成功。
- 201 Created:資源已創建。
- 204 No Content:請求成功,但沒有返回內容。
- 400 Bad Request:客戶端請求有誤。
- 401 Unauthorized:未授權。
- 403 Forbidden:禁止訪問。
- 404 Not Found:資源未找到。
- 500 Internal Server Error:服務器內部錯誤。
無狀態
RESTful 服務是無狀態的,即每次請求都必須包含所有必要的信息,服務器不會保存任何客戶端的狀態信息。
統一接口
所有的操作都通過統一的接口進行,即 HTTP 方法和 URI。
假設我們有一個用戶管理系統,以下是一些 RESTful 風格的 API 示例:
- 獲取所有用戶
http GET /users
- 獲取單個用戶
http GET /users/{id}
- 創建用戶
http POST /users
- 更新用戶
http PUT /users/{id}
- 刪除用戶
http DELETE /users/{id}
好處
- 簡潔明了:URI 和 HTTP 方法的結合使得 API 設計簡潔明了。
- 可擴展性強:易于添加新的資源和操作。
- 無狀態:每個請求都是獨立的,便于緩存和分布式處理。
- 標準化:遵循標準的 HTTP 方法和狀態碼,便于理解和使用。
實現
在 Java 開發中,可以使用 Spring Boot 等框架來實現 RESTful 風格的 API。以下是一個簡單的 Spring Boot 控制器示例:
@RestController @RequestMapping("/users")
public class UserController { @GetMapping public List<User> getAllUsers() { // 返回所有用戶 } @GetMapping("/{id}") public User getUser(@PathVariable Long id) { // 返回指定 ID 的用戶 } @PostMapping public User createUser(@RequestBody User user) { // 創建新用戶 } @PutMapping("/{id}") public User updateUser(@PathVariable Long id, @RequestBody User user) { // 更新指定 ID 的用戶 } @DeleteMapping("/{id}") public void deleteUser(@PathVariable Long id) { // 刪除指定 ID 的用戶 }
}