自定义穿梭框布局--两侧各自有分页
HTML
html
<!-- 自定义穿梭框布局 -->
<div class="custom-transfer-container">
<!-- 左侧面板:待选设备(分页) -->
<div class="transfer-panel left-panel">
<div class="panel-header">
<h3>待选设备</h3>
<el-input
v-model="leftFilterText"
placeholder="输入设备名"
clearable
size="small"
style="width: 200px"
>
<template #prefix>
<Search />
</template>
</el-input>
</div>
<div class="panel-body">
<el-table
ref="leftTableRef"
:data="filteredLeftData"
height="420"
@selection-change="handleLeftSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="deviceName" label="设备名称" />
</el-table>
</div>
</div>
<!-- 中间操作按钮 -->
<div class="transfer-buttons">
<el-button
type="primary"
:disabled="leftSelected.length === 0"
size="small"
@click="addToRight"
>
<el-icon>
<ArrowRightBold />
</el-icon>
</el-button>
<el-button
type="primary"
:disabled="rightSelected.length === 0"
size="small"
@click="removeFromRight"
>
<el-icon>
<ArrowLeftBold />
</el-icon>
</el-button>
</div>
<!-- 右侧面板:已选设备(不分页,滚动) -->
<div class="transfer-panel right-panel">
<div class="panel-header">
<h3>已选设备</h3>
</div>
<div class="panel-body">
<el-table
ref="rightTableRef"
:data="rightPanelData"
height="420"
@selection-change="handleRightSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="deviceName" label="设备名称" />
</el-table>
</div>
</div>
</div>
<!-- 左侧分页 -->
<div style="display: flex; justify-content: space-between; width: 100%">
<div class="left-pagination">
<el-pagination
v-if="associationTotal > 0"
:current-page="associationPage"
:page-size="associationPageSize"
:total="associationTotal"
layout="total, prev, pager, next, sizes"
size="small"
@current-change="
(page) => {
associationPage = page;
}
"
@size-change="
(size) => {
associationPageSize = size;
associationPage = 1;
}
"
/>
</div>
<!-- 右侧分页 -->
<div class="right-pagination">
<el-pagination
v-if="rightPanelTotal > 0"
:current-page="rightPage"
:page-size="rightPageSize"
:total="rightPanelTotal"
layout="total, prev, pager, next, sizes"
size="small"
@current-change="
(page) => {
rightPage = page;
}
"
@size-change="
(size) => {
rightPageSize = size;
rightPage = 1;
}
"
/>
</div>
</div>
JS
html
const dataShuttle = ref<DevicePageVO[]>([]); // 待选设备的数据
const valueShuttle = ref<number[]>([]); // 已选设备的 ID 列表
// 左侧面板相关
const leftTableRef = ref();
const leftFilterText = ref("");
const leftSelected = ref<DevicePageVO[]>([]);
// 右侧面板相关
const rightTableRef = ref();
const rightSelected = ref<DevicePageVO[]>([]);
// 计算左侧面板数据(分页后的数据)
const leftPanelData = computed(() => {
const startIndex = (associationPage.value - 1) * associationPageSize.value;
const endIndex = startIndex + associationPageSize.value;
return dataShuttle.value.slice(startIndex, endIndex);
});
// 计算过滤后的左侧数据
const filteredLeftData = computed(() => {
if (!leftFilterText.value) return leftPanelData.value;
return leftPanelData.value.filter((item) =>
item.deviceName?.toLowerCase().includes(leftFilterText.value.toLowerCase())
);
});
// 计算右侧面板数据(分页后的已选择设备)
const rightPanelData = computed(() => {
const allRightData = dataShuttle.value.filter((item) => valueShuttle.value.includes(item.id));
const startIndex = (rightPage.value - 1) * rightPageSize.value;
const endIndex = startIndex + rightPageSize.value;
return allRightData.slice(startIndex, endIndex);
});
// 计算右侧面板总数
const rightPanelTotal = computed(() => {
return dataShuttle.value.filter((item) => valueShuttle.value.includes(item.id)).length;
});
// 左侧面板选择变化
const handleLeftSelectionChange = (selection: DevicePageVO[]) => {
leftSelected.value = selection;
};
// 右侧面板选择变化
const handleRightSelectionChange = (selection: DevicePageVO[]) => {
rightSelected.value = selection;
};
// 添加到右侧
const addToRight = () => {
const newIds = leftSelected.value
.map((item) => item.id)
.filter((id) => id !== undefined) as number[];
valueShuttle.value = [...valueShuttle.value, ...newIds];
leftSelected.value = [];
leftTableRef.value?.clearSelection();
};
// 从右侧移除
const removeFromRight = () => {
const removeIds = rightSelected.value
.map((item) => item.id)
.filter((id) => id !== undefined) as number[];
valueShuttle.value = valueShuttle.value.filter((id) => !removeIds.includes(id));
rightSelected.value = [];
rightTableRef.value?.clearSelection();
};
// 表格数据
async function loadAssociationTable() {
loadingDialog.value = true;
loadingAssociationTable.value = true;
try {
// 获取所有数据,不分页,用于穿梭框显示
const res = await DeviceAPI.getPage({
roomIds: searchKeyword.value,
pageNum: 1,
pageSize: 1000, // 设置一个较大的值来获取所有数据
});
dataShuttle.value = res.list.map((item) => ({
...item,
key: item.id,
})) as DevicePageVO[];
// 设置已选中的设备
valueShuttle.value = dataShuttle.value.filter((item) => item.isMaster).map((item) => item.id);
// 设置总数为实际数据总数
associationTotal.value = dataShuttle.value.length;
// console.log("dataShuttle.value", dataShuttle.value);
} finally {
loadingAssociationTable.value = false; // 如果有加载状态变量
loadingDialog.value = false;
}
}
// 自定义渲染函数
const renderContent = (h: any, option: DevicePageVO) => {
// 使用 h 函数创建多列布局
return h("div", { class: "custom-transfer-item" }, [
h("span", { class: "column device-name" }, option.deviceName), // 设备名称
]);
};
style
html
.device-dialog-content {
max-height: 600px;
margin-bottom: 30px;
// overflow-x: hidden;
}
.search-area {
margin-top: 10px;
margin-bottom: 20px;
}/* 自定义穿梭框容器 */
.custom-transfer-container {
display: flex;
gap: 20px;
align-items: flex-start;
margin: 20px 0;
}
/* 穿梭框面板 */
.transfer-panel {
flex: 1;
border: 1px solid var(--el-border-color);
border-radius: 6px;
// background: #fff;
background-color: var(--el-bg-color);
}/* 面板头部 */
.panel-header {
padding: 12px 16px;
border-bottom: 1px solid var(--el-border-color);
// background: #f5f7fa;
background-color: var(--el-bg-color);
display: flex;
justify-content: space-between;
align-items: center;
}/* 面板内容 */
.panel-body {
padding: 0;
}/* 中间按钮区域 */
.transfer-buttons {
display: flex;
flex-direction: column;
gap: 10px;
justify-content: center;
padding: 20px 0;
margin-top: 18%;
}
.transfer-buttons .el-button {
width: 32px;
height: 32px;
padding: 0;
margin-left: 0px;
}
/* 分页样式 */
.left-pagination {
text-align: center;
float: left;
}