mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-08-30 14:52:51 +08:00
Add VLAN overview table to VLAN configuration page
Below the configuration form, a new table lists all configured VLANs with their port memberships: Member, Tagged, Untagged, and PVID columns. Port ranges are formatted compactly (e.g. "1-2,5"). Port-to-bit mapping uses the existing physToLogPort[] array populated by /status.json, so the table is consistent with the icon view in fetchVLAN() and works on any supported machine. Each row has a delete button that sends "vlan N d" via /cmd, with a confirmation dialog. VLAN 1 cannot be deleted (no button shown). Table and dropdown both refresh automatically after Update/Create and after delete via a new refreshVlanViews() helper. The N+1 request pattern (1x /vlanlist + Nx /vlan.json) keeps the backend simple and avoids buffer overflow risks for systems with many VLANs. For typical configurations (<50 VLANs), page load remains under one second.
This commit is contained in:
@@ -163,3 +163,4 @@ margin: 30px 0;
|
|||||||
}
|
}
|
||||||
select { text-align-last: right; font-family: monospace}
|
select { text-align-last: right; font-family: monospace}
|
||||||
option { direction: rtl; font-family: sans-serif}
|
option { direction: rtl; font-family: sans-serif}
|
||||||
|
#vlanTable td { text-align: left; }
|
||||||
|
|||||||
@@ -36,6 +36,22 @@
|
|||||||
<br/> <input style="width:40%;" class="action" id="vlan_sub" onclick="vlanSub();" type="button" value="Update / Create">
|
<br/> <input style="width:40%;" class="action" id="vlan_sub" onclick="vlanSub();" type="button" value="Update / Create">
|
||||||
<script src="/vlan_sub.js"></script>
|
<script src="/vlan_sub.js"></script>
|
||||||
</form>
|
</form>
|
||||||
|
<h2>Configured VLANs</h2>
|
||||||
|
<table id="vlanTable" style="width:90%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>VLAN</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Member Ports</th>
|
||||||
|
<th>Tagged Ports</th>
|
||||||
|
<th>Untagged Ports</th>
|
||||||
|
<th>PVID Ports</th>
|
||||||
|
<th>Delete</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="vlanTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<script src="/navigation.js"></script>
|
<script src="/navigation.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+90
-1
@@ -83,6 +83,95 @@ function fetchVLAN() {
|
|||||||
sendXHTTP(xhttp);
|
sendXHTTP(xhttp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function portsToRange(mask, nPorts) {
|
||||||
|
var parts = [];
|
||||||
|
var start = -1, prev = -1;
|
||||||
|
for (var p = 1; p <= nPorts; p++) {
|
||||||
|
var bit = physToLogPort[p - 1];
|
||||||
|
if ((mask >> bit) & 1) {
|
||||||
|
if (start < 0) start = p;
|
||||||
|
prev = p;
|
||||||
|
} else {
|
||||||
|
if (start >= 0) {
|
||||||
|
parts.push(start === prev ? String(start) : start + '-' + prev);
|
||||||
|
start = -1; prev = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start >= 0)
|
||||||
|
parts.push(start === prev ? String(start) : start + '-' + prev);
|
||||||
|
return parts.length ? parts.join(',') : '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadVlanTable() {
|
||||||
|
var tbody = document.getElementById('vlanTableBody');
|
||||||
|
if (!tbody) return;
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
var resp;
|
||||||
|
try { resp = await fetch('/vlanlist'); } catch(e) { return; }
|
||||||
|
if (!resp.ok) return;
|
||||||
|
var vlans = await resp.json();
|
||||||
|
for (var i = 0; i < vlans.length; i++) {
|
||||||
|
var v = vlans[i];
|
||||||
|
var vresp;
|
||||||
|
try { vresp = await fetch('/vlan.json?vid=' + v.id); } catch(e) { continue; }
|
||||||
|
if (!vresp.ok) continue;
|
||||||
|
var s = await vresp.json();
|
||||||
|
var m = parseInt(s.members, 16);
|
||||||
|
var members = m & 0x3FF;
|
||||||
|
var untag = (m >> 10) & 0x3FF;
|
||||||
|
var tagged = members & ~untag;
|
||||||
|
var pvid = parseInt(s.pvid, 16) & 0x3FF;
|
||||||
|
var tr = document.createElement('tr');
|
||||||
|
var td, a, btn;
|
||||||
|
td = document.createElement('td');
|
||||||
|
a = document.createElement('a');
|
||||||
|
a.href = '#';
|
||||||
|
a.textContent = v.id;
|
||||||
|
(function(vid) {
|
||||||
|
a.onclick = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
document.getElementById('vid').value = vid;
|
||||||
|
fetchVLAN();
|
||||||
|
};
|
||||||
|
})(v.id);
|
||||||
|
td.appendChild(a); tr.appendChild(td);
|
||||||
|
td = document.createElement('td');
|
||||||
|
td.textContent = v.name || ''; tr.appendChild(td);
|
||||||
|
td = document.createElement('td');
|
||||||
|
td.textContent = portsToRange(members, numPorts); tr.appendChild(td);
|
||||||
|
td = document.createElement('td');
|
||||||
|
td.textContent = portsToRange(tagged, numPorts); tr.appendChild(td);
|
||||||
|
td = document.createElement('td');
|
||||||
|
td.textContent = portsToRange(untag, numPorts); tr.appendChild(td);
|
||||||
|
td = document.createElement('td');
|
||||||
|
td.textContent = portsToRange(pvid, numPorts); tr.appendChild(td);
|
||||||
|
td = document.createElement('td');
|
||||||
|
if (v.id !== 1) {
|
||||||
|
btn = document.createElement('button');
|
||||||
|
btn.textContent = '✕';
|
||||||
|
(function(vid) {
|
||||||
|
btn.onclick = function() { deleteVlan(vid); };
|
||||||
|
})(v.id);
|
||||||
|
td.appendChild(btn);
|
||||||
|
}
|
||||||
|
tr.appendChild(td);
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteVlan(id) {
|
||||||
|
if (!confirm('Delete VLAN ' + id + '?')) return;
|
||||||
|
fetch('/cmd', { method: 'POST', body: 'vlan ' + id + ' d' })
|
||||||
|
.then(function() { refreshVlanViews(); })
|
||||||
|
.catch(function(err) { console.error('Delete failed:', err); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshVlanViews() {
|
||||||
|
loadVlanList();
|
||||||
|
loadVlanTable();
|
||||||
|
}
|
||||||
|
|
||||||
function loadVlanList() {
|
function loadVlanList() {
|
||||||
var xhttp = new XMLHttpRequest();
|
var xhttp = new XMLHttpRequest();
|
||||||
xhttp.onreadystatechange = function() {
|
xhttp.onreadystatechange = function() {
|
||||||
@@ -112,7 +201,7 @@ function loadVlanList() {
|
|||||||
window.addEventListener("load", function() {
|
window.addEventListener("load", function() {
|
||||||
update( () => {
|
update( () => {
|
||||||
vlanForm();
|
vlanForm();
|
||||||
loadVlanList();
|
refreshVlanViews();
|
||||||
document.getElementById('vlanSelect').onchange = function() {
|
document.getElementById('vlanSelect').onchange = function() {
|
||||||
document.getElementById('vid').value = this.value;
|
document.getElementById('vid').value = this.value;
|
||||||
fetchVLAN();
|
fetchVLAN();
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ async function vlanSub() {
|
|||||||
});
|
});
|
||||||
console.log('Completed!', response);
|
console.log('Completed!', response);
|
||||||
}
|
}
|
||||||
loadVlanList();
|
refreshVlanViews();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(`Error: ${err}`);
|
console.error(`Error: ${err}`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user