Vue3 + ElementPlus動態(tài)合并數(shù)據(jù)相同的單元格的完整代碼
最近的新項目有個需求需要合并單元列表。ElementPlus 的 Table 提供了合并行或列的方法,可以參考一下https://element-plus.org/zh-CN/component/table.html
但項目中,后臺數(shù)據(jù)返回格式和指定合并是動態(tài)且沒有規(guī)律的,Element 的示例過于簡單,因此記錄下來,大家可以參考一下!
效果圖
后臺返回的數(shù)據(jù)結構
代碼詳解
實操中,需要合并的代碼通常就是 list_cnt 數(shù)據(jù)需要進行合并,因為后臺返回的格式都是Data 數(shù)據(jù)中包裹著 list_cnt 數(shù)據(jù),這種格式看起來也是比較清晰。由 Element 文檔可知:el-table 組件主要靠 :span-method 方法實現(xiàn)合并。
完整代碼
<template> <div class="app-container"> <div class="search-bar"> <el-form :inline="true" :model="formData" class="common-form-inline"> <el-form-item label="名稱搜索"> <el-input v-model="formData.name" clearable @clear="queryAndroidList(true)" placeholder="請輸入" /> </el-form-item> <el-form-item> <el-button type="primary" @click="queryAndroidList(true)">搜索</el-button> </el-form-item> </el-form> </div> <el-table :data="list" :stripe="true" fit highlight-current-row :show-overflow-tooltip="true" style="width: 100%; margin-top: 20px" v-loading="loading" @selection-change="handleSelectionChange" :span-method="objectSpanMethod" border> <el-table-column type="selection" align="center" width="55" /> <el-table-column align="center" label="實例" prop="idx" width="220px"> <template #default="scope"> <el-button size="small" round>實例: {{ scope.row.idx }}</el-button> <el-button type="primary" size="small" @click="handleEdit(scope.row)">編輯</el-button> <el-button type="primary" size="small" @click="handleCreate(scope.row)">創(chuàng)建</el-button> <div class="time-line"> <span>到期時間: {{ scope.row.update_time || '未授權' }}</span> </div> </template> </el-table-column> <el-table-column align="center" label="ip" prop="ip" width="180px"> </el-table-column> <el-table-column align="center" label="ADB/API端口" prop="levelName" width="180px"> <template #default="scope"> <span v-if="scope.row.adb_port || scope.row.sdk_port">{{ scope.row.adb_port || '-' }} - {{ scope.row.sdk_port || '-' }}</span> <span v-else> - </span> </template> </el-table-column> <el-table-column align="center" label="名稱" prop="name" width="120px"> <template #default="scope"> <span v-if="scope.row.name">{{ scope.row.name }}</span> <span v-else>-</span> </template> </el-table-column> <el-table-column align="center" label="狀態(tài)" prop="status" width="150px"> <template #default="scope"> <el-button plain :style="{ backgroundColor: scope.row.status === 20 ? '#fef0f0' : scope.row.status === 10 ? '#f0f9eb' : '', borderColor: scope.row.status === 20 ? '#fde2e2' : scope.row.status === 10 ? '#e1f3d8' : '', color: scope.row.status === 20 ? '#f56c6c' : scope.row.status === 10 ? '#67c23a' : '' }" v-if="scope.row.status"> {{ scope.row.status === 10 ? '運行中' : scope.row.status === 20 ? '關機' : '空閑' }} </el-button> </template> </el-table-column> <el-table-column align="center" label="系統(tǒng)版本" prop="serial_no" width="150px"> <template #default="scope"> <span>版本1.0</span> </template> </el-table-column> <el-table-column :show-overflow-tooltip="false" align="center" label="操作"> <template #default="scope"> <div class="cell"> <el-button :type="scope.row.status === 20 ? 'success' : 'danger'" size="small" @click="handlePowerAction(scope.row)"> {{ scope.row.status === 20 ? '開機' : '關機' }} </el-button> <div class="el-dropdown flex flex-wrap items-center"> <el-dropdown> <el-button type="info"> 更多操作<el-icon class="el-icon--right"><arrow-down /></el-icon> </el-button> <template #dropdown> <el-dropdown-menu> <el-dropdown-item @click="handleoperate('restart', scope.row)">重啟云機 </el-dropdown-item> <el-dropdown-item @click="handleoperate('edit', scope.row)">修改名稱</el-dropdown-item> <el-dropdown-item @click="handleoperate('remark', scope.row)">設置備注</el-dropdown-item> <el-dropdown-item @click="handleoperate('random', scope.row)">隨機設備信息</el-dropdown-item> <el-dropdown-item @click="handleoperate('mirror', scope.row)">切換鏡像</el-dropdown-item> <el-dropdown-item @click="handleoperate('reset', scope.row)">重置云機</el-dropdown-item> <el-dropdown-item @click="handleoperate('copy', scope.row)">復制云機</el-dropdown-item> <el-dropdown-item @click="handleoperate('delete', scope.row)">刪除云機</el-dropdown-item> <el-dropdown-item @click="handleoperate('terminal', scope.row)">終端窗口</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </div> <div class="flex flex-wrap items-center"> <el-dropdown> <el-button type="primary"> 選擇網(wǎng)絡<el-icon class="el-icon--right"><arrow-down /></el-icon> </el-button> <template #dropdown> <el-dropdown-menu> <el-dropdown-item @click="handleSelectVPC">(舊)選擇VPC網(wǎng)絡</el-dropdown-item> <el-dropdown-item>(新)選擇VPC網(wǎng)絡</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </div> </div> </template> </el-table-column> </el-table> <!-- 創(chuàng)建 --> <el-dialog v-model="createdVisible" title="創(chuàng)建安卓" width="500"> <el-form :model="formCreate"> <el-form-item label="云機數(shù)量" :label-width="formLabelWidth"> <el-input-number v-model="formCreate.num" autocomplete="off" :min="1" :max="12" /> </el-form-item> <el-form-item label="鏡像類型" :label-width="formLabelWidth"> <el-radio-group v-model="formCreate.img_type"> <el-radio label="10">基礎鏡像</el-radio> <el-radio label="20">GMS鏡像</el-radio> </el-radio-group> <el-button type="primary" size="small" style="margin-left: 10px" @click="handleSwitchImage"> <el-icon> <Refresh /> </el-icon> 切換 </el-button> </el-form-item> <el-form-item label="DNS設置" :label-width="formLabelWidth"> <el-select v-model="formCreate.dns" @change="selectDns" placeholder="請選擇DNS" class="w130" filterable> <el-option v-for="item in setDns" :key="item.id" :label="item.name" :value="item.id" /> </el-select> </el-form-item> <el-form-item label="屏幕刷新率" :label-width="formLabelWidth"> <el-select v-model="formCreate.fps" placeholder="請選擇刷新率"> <el-option label="60 FPS" value="60" /> <el-option label="90 FPS" value="90" /> <el-option label="120 FPS" value="120" /> </el-select> </el-form-item> <!-- <el-form-item label="VPC網(wǎng)絡" :label-width="formLabelWidth"> <el-select v-model="formCreate.vpc" placeholder="請選擇VPC"> <el-option label="VPC網(wǎng)絡 1" value="vpc1" /> <el-option label="VPC網(wǎng)絡 2" value="vpc2" /> <el-option label="VPC網(wǎng)絡 3" value="vpc3" /> </el-select> </el-form-item> --> </el-form> <template #footer> <div class="dialog-footer"> <el-button @click="createdVisible = false">取消</el-button> <el-button type="primary" @click="createdDialog(row)"> 確定 </el-button> </div> </template> </el-dialog> <!-- 修改 --> <el-dialog v-model="dialogFormVisible" title="修改云機名稱" width="500"> <el-form :model="formEdit"> <el-form-item label="名稱" :label-width="formLabelWidth"> <el-input v-model="formEdit.new_name" autocomplete="off" /> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button @click="dialogFormVisible = false">取消</el-button> <el-button type="primary" @click="editDialog(row)"> 確定 </el-button> </div> </template> </el-dialog> <!-- 設置備注 --> <el-dialog v-model="remarkVisible" title="設置云機備注" width="500"> <el-form :model="formRemark"> <el-form-item label="云機備注" :label-width="formLabelWidth"> <el-input v-model="formRemark.name" autocomplete="off" /> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button @click="remarkVisible = false">取消</el-button> <el-button type="primary" @click="remarkVisible = false"> 確定 </el-button> </div> </template> </el-dialog> <!-- 切換云機鏡像 --> <el-dialog v-model="mirrorVisible" title="切換云機鏡像" width="500"> <el-form :model="formMirror"> <el-form-item label="云機鏡像" :label-width="formLabelWidth"> <el-select v-model="formMirror.mirror" multiple placeholder="請選擇" style="width: 240px"> <el-option v-for="item in mirrorList" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <p>說明:如何切換的鏡像不存在,系統(tǒng)會先拉取鏡像,這個過程比較耗時請耐心等待。</p> </el-form> <template #footer> <div class="dialog-footer"> <el-button @click="mirrorVisible = false">取消</el-button> <el-button type="primary" @click="mirrorVisible = false"> 確定 </el-button> </div> </template> </el-dialog> <!-- 隨機設備信息 --> <el-dialog v-model="randomVisible" title="隨機設備信息" width="500"> </el-dialog> <!-- 復制云機 --> <el-dialog v-model="copyVisible" title="復制云機" width="500"> <el-form :model="formCopy"> <span>云機復制數(shù)量 </span><el-input-number v-model="formCopy.num" :min="1" :max="10" /> </el-form> <span>說明:復制請先關閉云機。相同實例號的云機,同時只能有一臺為開機狀態(tài)。復制云機比較耗時請耐心等待</span> <template #footer> <div class="dialog-footer"> <el-button @click="copyVisible = false">取消</el-button> <el-button type="primary" @click="handleCopy(row)"> 確定 </el-button> </div> </template> </el-dialog> <!-- 重置云機 --> <el-dialog v-model="resetVisible" title="提示" width="500"> <el-icon> <WarningFilled /> </el-icon> <span>確定要重置此云機?</span> <template #footer> <div class="dialog-footer"> <el-button @click="resetVisible = false">取消</el-button> <el-button type="primary" @click="handleReset(row)"> 確定 </el-button> </div> </template> </el-dialog> <!-- 刪除云機 --> <el-dialog v-model="deleteVisible" title="提示" width="500"> <el-icon> <WarningFilled /> </el-icon> <span>確定要刪除此云機?</span> <template #footer> <div class="dialog-footer"> <el-button @click="deleteVisible = false">取消</el-button> <el-button type="primary" @click="handleDele(row)"> 確定 </el-button> </div> </template> </el-dialog> <!-- 終端窗口 --> <el-dialog v-model="terminalVisible" title="終端窗口" width="500"> <iframe src="http://192.168.1.100:8080" frameborder="0" width="100%" height="500px"></iframe> <template #footer> </template> </el-dialog> <!-- 選擇網(wǎng)絡 --> <el-dialog v-model="networkVisible" title="選擇網(wǎng)絡" width="500"> <el-form :model="formNetwork"> <el-form-item label="VPC網(wǎng)絡" :label-width="formLabelWidth"> <el-select v-model="formNetwork.network" placeholder="請選擇"> <el-option label="VPC網(wǎng)絡1" value="1" /> <el-option label="VPC網(wǎng)絡2" value="2" /> <el-option label="VPC網(wǎng)絡3" value="3" /> </el-select> </el-form-item> </el-form> </el-dialog> </div> </template> <script setup> import { ref, reactive, nextTick } from 'vue' import andriodList from '@/network/andriodList' import { ElMessage } from 'element-plus' // 搜索條件 const formData = reactive({ name: '', }) const loading = ref(false); // 列表的加載中 const list = ref([{}]) // 列表的數(shù)據(jù) const queryAndroidList = async (flag) => { // 根據(jù)搜索條件設置查詢參數(shù) if (flag) { formData.name = formData.name.trim() } loading.value = true try { const res = await andriodList.getAllAndroidList({ name: formData.name }) if (res.code === 200) { let allDataList = []; res.data && res.data.length > 0 && res.data.forEach((item, index) => { item.list_cnt && item.list_cnt.length > 0 && item.list_cnt.forEach((item2, index2) => { allDataList.push({ ...item, // ...item2, 看具體需求 處理列表所需字段, 將list_cnt里的數(shù)據(jù)平鋪開 idx: item2.idx, name: item2.name, status: item2.status, data_dir: item2.data_dir, update_time: item2.update_time, sdk_port1: item2.sdk_port, adb_port1: item2.adb_port, cnt_id1: item2.cnt_id, }) }) }) list.value = allDataList; } else { list.value = [] } } catch (error) { list.value = [] } finally { loading.value = false } } // 初始化獲取列表 queryAndroidList() const selectIds = ref([]); // 行復選框選中項變化 function handleSelectVPC(selection) { selectIds.value = selection.map(item => item.id); } function handleQuery() { loading.value = true; } // 多選框選中數(shù)據(jù) function handleSelectionChange(selection) { selectIds.value = selection.map(item => item.id); } /** * 合并行或列 * @param row 行號 * @param col 列號 * @param rowspan 行合并數(shù) * @param colspan 列合并數(shù) * @param rowIndex 當前行號 * @param columnIndex 當前列號 * */ const objectSpanMethod = ({ row, column, rowIndex, columnIndex, }) => { if (column.property === 'idx') { if (rowIndex > 0 && list.value[rowIndex].idx === list.value[rowIndex - 1].idx) { return { rowspan: 0, colspan: 0, } } return { rowspan: getRowspan('idx', rowIndex), colspan: 1, } } } // 獲取行合并數(shù) const getRowspan = (key, rowIndex) => { let rowspan = 1; //默認合并1行 let curVal = list.value[rowIndex][key]; //存儲了當前值 for (let i = rowIndex + 1; i < list.value.length; i++) { if (list.value[i][key] === curVal) { rowspan++; } else { break; } } return rowspan; } // 選擇DNS const setDns = ref([{ id: '1', name: 'DNS1' }, { id: '2', name: 'DNS2' }]) const selectDns = async () => { } //創(chuàng)建彈窗 const formCreate = reactive({ idx: '', num: 1, img_type: "10", dns: '', fps: '', }) const createdVisible = ref(false) // 創(chuàng)建實例 function handleCreate(row) { createdVisible.value = true; formCreate.idx = row.idx; // 保存idx到formCreate中 } const handleCreateAndroid = async () => { try { const res = await andriodList.createAPI({ idx: formCreate.idx, // 使用保存的idx num: formCreate.num, img_type: formCreate.img_type, dns: formCreate.dns, fps: formCreate.fps, }) if (res.code === 200) { ElMessage.success(res.msg) createdVisible.value = false // 刷新列表 queryAndroidList() } else { ElMessage.error(res.msg) } } catch (error) { ElMessage.error(res.msg) } } //確定創(chuàng)建彈窗 function createdDialog() { handleCreateAndroid() } // 編輯實例 function handleEdit(row) { console.log('編輯實例:', row); } //切換鏡像 const handleSwitchImage = () => { console.log('切換鏡像'); } //開機--關機 --status 容器狀態(tài) 10 運行中 20 關機 let runId = null async function handlePowerAction(row) { runId = row.cnt_id1 const status = row.status try { let res if (status === 10) { res = await andriodList.stopAPI({ cnt_id: runId }) } else { res = await andriodList.runAPI({ cnt_id: runId }) } if (res.code === 200) { ElMessage.success(res.msg) queryAndroidList() } else { ElMessage.error(res.msg) } } catch (error) { ElMessage.error(res.msg) } } //重啟云機 const restarCnt = async () => { try { const res = await andriodList.restart({ cnt_id: publicId }) if (res.code === 200) { ElMessage.success(res.msg) // 重啟成功后重新獲取列表 queryAndroidList() } else { ElMessage.error(res.msg) } } catch (error) { ElMessage.error(res.msg) } } const formEdit = reactive({ cnt_id: '', new_name: '', }) const dialogFormVisible = ref(false) const formLabelWidth = '140px' const handleEditName = async () => { try { const res = await andriodList.renameAPI({ cnt_id: formEdit.cnt_id, new_name: formEdit.new_name }) if (res.code === 200) { ElMessage.success(res.msg) dialogFormVisible.value = false queryAndroidList() } else { ElMessage.error(res.msg) } } catch (error) { ElMessage.error(res.msg) } } //修改彈窗 function editDialog() { handleEditName() } //設置備注 const formRemark = reactive({ name: '', }) const remarkVisible = ref(false) //切換 const formMirror = reactive({ mirror: '', }) const mirrorList = [{ value: '1', label: '鏡像1' }] const mirrorVisible = ref(false) //隨機 const randomVisible = ref(false) //復制 const formCopy = reactive({ num: 1, src_cnt_id: "", target_cnt_idx: "", target_cnt_name: "", }) const copyVisible = ref(false) //復制彈窗 function handleCopy() { handleCopyAPI() } const handleCopyAPI = async () => { try { const res = await andriodList.copyAPI({ num: formCopy.num, src_cnt_id: formCopy.src_cnt_id, target_cnt_idx: formCopy.target_cnt_idx, target_cnt_name: formCopy.target_cnt_name, }) if (res.code === 200) { ElMessage.success(res.msg) copyVisible.value = false queryAndroidList() } else { ElMessage.error(res.msg) } } catch (error) { ElMessage.error(res.msg) } } //重置 const resetVisible = ref(false) const handleResetId = async () => { try { const res = await andriodList.resetAPI({ cnt_id: publicId }) if (res.code === 200) { ElMessage.success(res.msg) deleteVisible.value = false queryAndroidList() } else { ElMessage.error(res.msg) } } catch (error) { ElMessage.error(res.msg) } } //重置彈窗 function handleReset() { handleResetId() } //刪除 const deleteVisible = ref(false) const handleDeleId = async () => { try { const res = await andriodList.deleteCntAPI({ cnt_id: publicId }) if (res.code === 200) { ElMessage.success(res.msg) deleteVisible.value = false queryAndroidList() } else { ElMessage.error(res.msg) } } catch (error) { ElMessage.error(res.msg) } } //刪除 function handleDele() { handleDeleId() } //終端 const terminalVisible = ref(false) //選擇網(wǎng)絡 const formNetwork = reactive({ network: '', }) const networkVisible = ref(false) //選擇網(wǎng)絡彈窗 //操作 let publicId = null // 公共id function handleoperate(type, row) { publicId = row.cnt_id1 // 獲取第一個云機的cnt_id switch (type) { case 'restart': restarCnt(row) break; case 'edit': dialogFormVisible.value = true; formEdit.cnt_id = publicId; formEdit.new_name = row.name; break; case 'remark': remarkVisible.value = true; break; case 'random': randomVisible.value = true; break; case 'mirror': mirrorVisible.value = true; break; case 'copy': copyVisible.value = true; formCopy.src_cnt_id = publicId; formCopy.target_cnt_name = row.name; formCopy.target_cnt_idx = row.idx; break; case 'terminal': terminalVisible.value = true; break; case 'reset': // 處理重置操作 resetVisible.value = true; break; case 'delete': // 處理刪除操作 deleteVisible.value = true; break; default: break; } } </script> <style lang="scss" scoped> .app-container { .search-bar { .el-icon { color: #fff; } } .cell { display: flex; justify-content: center; align-items: center; } .el-dropdown { margin-left: 10px; } .examples { display: flex; justify-content: center; } .time-line { margin-top: 10px; color: rgb(235, 0, 0); font-size: 14px; } } </style>
代碼中會有一些注釋,根據(jù)個人需求可以進行參考,此需求也涉及到按鈕操作的,如果沒有次需求可以忽略不看。
以上就是列表的合并單元格,如果對你有幫助,麻煩點個贊唄~
到此這篇關于Vue3 + ElementPlus動態(tài)合并數(shù)據(jù)相同的單元格的文章就介紹到這了,更多相關Vue3 ElementPlus動態(tài)合并單元格內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Vue3解決ElementPlus自動導入時ElMessage無法顯示的問題
這篇文章主要介紹了Vue3解決ElementPlus自動導入時ElMessage無法顯示的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03Vue公共loading升級版解決思路(處理并發(fā)異步差時響應)
這篇文章主要介紹了Vue公共loading升級版(處理并發(fā)異步差時響應),解決思路是通過定義一個全局對象來存儲每個接口的響應狀態(tài),直到每個請求接口都收到響應才變更狀態(tài),結束loading動畫,需要的朋友可以參考下2023-11-11