在Web应用开发中,多对多记录关联是非常常见的需求,比如一个用户可以拥有多个角色,一个角色也可以分配给多个用户,这类场景就需要在UI层面实现多个记录到多个记录的关联操作。下面我们就一步步实现这个功能。

实现思路
要实现多对多记录关联的UI,核心逻辑分为几个部分:首先需要展示待关联的两组记录列表,然后提供选择交互让用户选中需要关联的记录,最后将选中的关联关系动态展示出来,同时支持取消关联操作。
基础页面结构
我们先搭建基础的HTML结构,分为左侧待选列表、中间操作区域、右侧已关联列表三个部分:
<div class="container">
<div class="list-section">
<h3>待选用户</h3>
<ul id="userList">
<li data-id="1">张三</li>
<li data-id="2">李四</li>
<li data-id="3">王五</li>
</ul>
</div>
<div class="operate-section">
<button id="addBtn">关联到选中角色></button>
<button id="removeBtn">移除选中关联</button>
</div>
<div class="list-section">
<h3>待选角色</h3>
<ul id="roleList">
<li data-id="101">管理员</li>
<li data-id="102">编辑</li>
<li data-id="103">访客</li>
</ul>
</div>
</div>
<div class="result-section">
<h3>已关联关系</h3>
<ul id="relationList"></ul>
</div>样式补充
为了让列表可交互,我们给可点击的列表项添加基础样式,区分选中状态:
.container {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.list-section {
width: 200px;
border: 1px solid #ccc;
padding: 10px;
}
.list-section ul {
list-style: none;
padding: 0;
}
.list-section li {
padding: 8px;
margin: 5px 0;
border: 1px solid #eee;
cursor: pointer;
}
.list-section li.selected {
background-color: #e6f7ff;
border-color: #1890ff;
}
.operate-section {
display: flex;
flex-direction: column;
justify-content: center;
gap: 10px;
}
.result-section {
border: 1px solid #ccc;
padding: 10px;
width: 600px;
}
.result-section li {
padding: 8px;
margin: 5px 0;
border-bottom: 1px solid #eee;
}交互逻辑实现
接下来编写JavaScript逻辑,实现列表项选中、关联关系添加和移除的功能:
// 存储当前选中的用户和角色
let selectedUser = null;
let selectedRole = null;
// 存储所有关联关系
let relations = [];
// 初始化列表项选中事件
function initSelectEvent(listId) {
const list = document.getElementById(listId);
list.addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
// 清除同列表其他选中状态
const items = list.querySelectorAll('li');
items.forEach(item => item.classList.remove('selected'));
// 设置当前选中
e.target.classList.add('selected');
// 根据列表id存储选中值
if (listId === 'userList') {
selectedUser = { id: e.target.dataset.id, name: e.target.textContent };
} else if (listId === 'roleList') {
selectedRole = { id: e.target.dataset.id, name: e.target.textContent };
}
}
});
}
// 添加关联关系
function addRelation() {
if (!selectedUser || !selectedRole) {
alert('请先选中用户和角色');
return;
}
// 检查是否已存在相同关联
const exist = relations.some(item => item.userId === selectedUser.id && item.roleId === selectedRole.id);
if (exist) {
alert('该关联关系已存在');
return;
}
// 添加关联
relations.push({
userId: selectedUser.id,
userName: selectedUser.name,
roleId: selectedRole.id,
roleName: selectedRole.name
});
// 更新展示
renderRelations();
}
// 移除关联关系
function removeRelation() {
const relationList = document.getElementById('relationList');
const selectedRelation = relationList.querySelector('.selected');
if (!selectedRelation) {
alert('请先选中要移除的关联关系');
return;
}
const index = parseInt(selectedRelation.dataset.index);
relations.splice(index, 1);
// 更新展示
renderRelations();
}
// 渲染已关联关系列表
function renderRelations() {
const relationList = document.getElementById('relationList');
relationList.innerHTML = '';
relations.forEach((item, index) => {
const li = document.createElement('li');
li.textContent = `${item.userName} - ${item.roleName}`;
li.dataset.index = index;
li.addEventListener('click', function() {
const items = relationList.querySelectorAll('li');
items.forEach(i => i.classList.remove('selected'));
this.classList.add('selected');
});
relationList.appendChild(li);
});
}
// 初始化事件绑定
initSelectEvent('userList');
initSelectEvent('roleList');
document.getElementById('addBtn').addEventListener('click', addRelation);
document.getElementById('removeBtn').addEventListener('click', removeRelation);功能说明
上述代码实现的功能逻辑如下:
- 点击左侧用户列表或右侧角色列表的条目,会高亮显示选中状态,同时存储当前选中的记录信息
- 选中一个用户和一个角色后,点击中间关联到选中角色按钮,会在下方已关联关系列表中新增一条关联记录
- 点击已关联关系列表中的条目选中后,点击移除选中关联按钮,会删除对应的关联关系
- 重复添加相同的关联关系时会给出提示,避免重复数据
扩展说明
如果需要支持同时选中多个用户或多个角色,只需要调整选中逻辑,将selectedUser和selectedRole改为数组存储,同时在添加关联时遍历数组生成所有可能的组合即可。如果数据需要从后端获取,只需要在页面加载时通过接口请求数据,动态生成对应的列表项就可以适配实际业务场景。
HTMLJavaScript多对多关联UI交互DOM操作修改时间:2026-06-02 03:57:58