Browse Source

설정 UI 모듈화 및 프로토콜 검증/파일 처리 개선

v2
최원형 3 months ago
parent
commit
605ee80abe
  1. 2
      src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/OdoSpeedField.java
  2. 2
      src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/OdsDirField.java
  3. 85
      src/main/resources/static/css/style.css
  4. 153
      src/main/resources/static/index.html
  5. 11
      src/main/resources/static/js/navigation.js
  6. 19
      src/main/resources/static/js/protocol/odometer.js
  7. 13
      src/main/resources/static/js/protocol/protocolValidation.js

2
src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/OdoSpeedField.java

@ -12,7 +12,7 @@ public class OdoSpeedField {
private String id; private String id;
private int shift; private Integer shift;
private String mask; private String mask;

2
src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/OdsDirField.java

@ -13,7 +13,7 @@ public class OdsDirField {
private String id; private String id;
private int shift; private Integer shift;
private String mask; private String mask;

85
src/main/resources/static/css/style.css

@ -54,12 +54,97 @@ details.nav {
background: #d9d9d9; background: #d9d9d9;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
padding: 0 32px; padding: 0 32px;
font-size: 28px; font-size: 28px;
font-weight: 500; font-weight: 500;
box-shadow: 0 2px 4px rgba(0, 0, 0, .15) inset box-shadow: 0 2px 4px rgba(0, 0, 0, .15) inset
} }
.topbar-actions {
display: flex;
align-items: center;
gap: 12px;
}
.topbar-file-actions {
display: flex;
align-items: center;
gap: 12px;
}
.topbar-actions .file-upload-wrapper-device {
display: flex;
align-items: center;
}
.topbar-actions .file-upload-wrapper-protocol {
display: flex;
align-items: center;
}
.topbar-actions button {
min-width: 140px;
height: 40px;
padding: 10px 18px;
line-height: 1;
font-size: 16px;
border: 1px solid #bbb;
border-radius: 4px;
cursor: pointer;
}
.topbar-actions .select-file-btn-device,
.topbar-actions .download-device-file {
background: #2f6fdf;
border-color: #245cc0;
color: #fff;
}
.topbar-actions .select-file-btn-device:hover,
.topbar-actions .download-device-file:hover {
background-color: #245cc0;
}
.topbar-actions .select-file-btn-device:active,
.topbar-actions .download-device-file:active {
background-color: #1d4da3;
transform: scale(0.97);
}
.topbar-actions .select-file-btn-protocol,
.topbar-actions .download-protocol-file {
background: #d97706;
border-color: #b85f00;
color: #fff;
}
.topbar-actions .select-file-btn-protocol:hover,
.topbar-actions .download-protocol-file:hover {
background-color: #b85f00;
}
.topbar-actions .select-file-btn-protocol:active,
.topbar-actions .download-protocol-file:active {
background-color: #924b00;
transform: scale(0.97);
}
.topbar-actions .save-file {
background-color: #4caf50;
border-color: #3f9a43;
color: #fff;
}
.topbar-actions .save-file:hover {
background-color: #45a049;
}
.topbar-actions .save-file:active {
background-color: #3e8e41;
transform: scale(0.97);
}
/* ───── Main Board ───── */ /* ───── Main Board ───── */
.content { .content {
flex: 1; flex: 1;

153
src/main/resources/static/index.html

@ -26,7 +26,7 @@
<button data-page="modbus" id="modbusSidebarBtn" style="display: none">MODBUS</button> <button data-page="modbus" id="modbusSidebarBtn" style="display: none">MODBUS</button>
<button data-page="opcua" id="opcUaSidebarBtn" style="display: none">OPC_UA</button> <button data-page="opcua" id="opcUaSidebarBtn" style="display: none">OPC_UA</button>
<button data-page="can" id="canSidebarBtn" style="display: none">CAN</button> <button data-page="can" id="canSidebarBtn" style="display: none">CAN</button>
<button data-page="odometer" id="odometerSidebarBtn" style="display: none">ODOMETER</button> <button data-page="odometer" id="odometerSidebarBtn" style="display: none">ODOmeter</button>
</div> </div>
</details> </details>
</aside> </aside>
@ -35,7 +35,31 @@
<div class="content"> <div class="content">
<header class="topbar"> <header class="topbar">
<span id="page-title">SSID</span> <span id="page-title">SSID</span>
<button class="save-file">Save</button> <div class="topbar-actions">
<div class="topbar-file-actions topbar-device-actions">
<div class="file-upload-wrapper-device"
data-file-type="device"
data-expected-filename="deviceConfig.json"
data-upload-url="/setting/device"
data-get-url="/setting/get-device">
<input type="file" class="upload-input-device" accept=".json" style="display: none;">
<button class="select-file-btn-device">Load Local Device File</button>
</div>
<button class="download-device-file">Download Local Device File</button>
</div>
<div class="topbar-file-actions topbar-protocol-actions" style="display: none;">
<div class="file-upload-wrapper-protocol"
data-file-type="protocol"
data-expected-filename="protocolConfig.json"
data-upload-url="/setting/protocol"
data-get-url="/setting/get-protocol">
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;">
<button class="select-file-btn-protocol">Load Local Protocol File</button>
</div>
<button class="download-protocol-file">Download Local Protocol File</button>
</div>
<button class="save-file">Save</button>
</div>
</header> </header>
<main class="board"> <main class="board">
@ -63,20 +87,9 @@
<div class="ssid-country-code"> <div class="ssid-country-code">
<label for="wifi_country_code">Country Code</label> <label for="wifi_country_code">Country Code</label>
<input id="wifi_country_code" maxlength="2" placeholder="KR" /> <input id="wifi_country_code" maxlength="2" placeholder="KR" />
<span class="field-hint">2-letter code such as KR, EU, JP, US</span> <span class="field-hint">2-letter code such as KR, DE, JP, US</span>
</div> </div>
</div> </div>
<div class="actions">
<div class="file-upload-wrapper-device"
data-file-type="device"
data-expected-filename="deviceConfig.json"
data-upload-url="/setting/device"
data-get-url="/setting/get-device">
<input type="file" class="upload-input-device" accept=".json" style="display: none;">
<button class="select-file-btn-device">Load Local Device File</button>
</div>
<button class="download-device-file">Download Local Device File</button>
</div>
</section> </section>
<!-- NETWORK PAGE --> <!-- NETWORK PAGE -->
@ -115,17 +128,6 @@
<div class="grid-3-col"><span>LTE Server</span> <input id="lte_server_ip" placeholder="ex)123.123.123.123"/><input <div class="grid-3-col"><span>LTE Server</span> <input id="lte_server_ip" placeholder="ex)123.123.123.123"/><input
id="lte_server_port"/></div> id="lte_server_port"/></div>
</div> </div>
<div class="actions">
<div class="file-upload-wrapper-device"
data-file-type="device"
data-expected-filename="deviceConfig.json"
data-upload-url="/setting/device"
data-get-url="/setting/get-device">
<input type="file" class="upload-input-device" accept=".json" style="display: none;">
<button class="select-file-btn-device">Load Local Device File</button>
</div>
<button class="download-device-file">Download Local Device File</button>
</div>
</section> </section>
<!-- I/O PAGE --> <!-- I/O PAGE -->
@ -215,17 +217,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="actions">
<div class="file-upload-wrapper-device"
data-file-type="device"
data-expected-filename="deviceConfig.json"
data-upload-url="/setting/device"
data-get-url="/setting/get-device">
<input type="file" class="upload-input-device" accept=".json" style="display: none;">
<button class="select-file-btn-device">Load Local Device File</button>
</div>
<button class="download-device-file">Download Local Device File</button>
</div>
</section> </section>
<!-- LOG PAGE --> <!-- LOG PAGE -->
@ -245,22 +236,11 @@
</div> </div>
</div> </div>
</div> </div>
<div class="actions">
<div class="file-upload-wrapper-device"
data-file-type="device"
data-expected-filename="deviceConfig.json"
data-upload-url="/setting/device"
data-get-url="/setting/get-device">
<input type="file" class="upload-input-device" accept=".json" style="display: none;">
<button class="select-file-btn-device">Load Local Device File</button>
</div>
<button class="download-device-file">Download Local Device File</button>
</div>
</section> </section>
<!-- Protocol PAGE --> <!-- Protocol PAGE -->
<section id="page-reg" class="page" style="display:none"> <section id="page-reg" class="page" style="display:none">
<div class="card"><h3>Config</h3> <div class="card">
<div class="io-group"> <div class="io-group">
<div class="reg-io-grid"> <div class="reg-io-grid">
<div class="io-fieldset"> <div class="io-fieldset">
@ -303,6 +283,13 @@
<option value="off">off</option> <option value="off">off</option>
</select> </select>
</div> </div>
<div class="io-row"><span class="label">ODOmeter on/off</span>
<select id="OdoMeter">
<option selected disabled hidden="hidden"></option>
<option value="on">on</option>
<option value="off">off</option>
</select>
</div>
<div class="io-row"><span class="label">Speed Data Source</span> <div class="io-row"><span class="label">Speed Data Source</span>
<select id="reg_speed_data_source"> <select id="reg_speed_data_source">
<option selected disabled hidden="hidden"></option> <option selected disabled hidden="hidden"></option>
@ -357,33 +344,15 @@
<input type="checkbox" id="di1"/>DI1 <input type="checkbox" id="di1"/>DI1
</label> </label>
</div> </div>
<div class="io-row"><span class="label">ODOmeter on/off</span>
<select id="OdoMeter">
<option selected disabled hidden="hidden"></option>
<option value="on">on</option>
<option value="off">off</option>
</select>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="config-actions">
<div class="file-upload-wrapper-protocol"
data-file-type="protocol"
data-expected-filename="protocolConfig.json"
data-upload-url="/setting/protocol"
data-get-url="/setting/get-protocol">
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;">
<button class="select-file-btn-protocol">Load Local Protocol File</button>
</div>
<button class="download-protocol-file">Download Local Protocol File</button>
</div>
</section> </section>
<section id="page-modbus" class="page" style="display:none;"> <section id="page-modbus" class="page" style="display:none;">
<div class="card"> <div class="card">
<h3>Modbus Configuration</h3>
<div class="modbus-table-container"> <div class="modbus-table-container">
<table> <table>
<thead> <thead>
@ -406,23 +375,10 @@
</div> </div>
</div> </div>
<div id="modbus-bottom-actions">
<div class="file-upload-wrapper-protocol"
data-file-type="protocol"
data-expected-filename="protocolConfig.json"
data-upload-url="/setting/protocol"
data-get-url="/setting/get-protocol">
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;">
<button class="select-file-btn-protocol">Load Local Protocol File</button>
</div>
<button class="download-protocol-file">Download Local Protocol File</button>
</div>
</section> </section>
<section id="page-opcua" class="page" style="display:none;"> <section id="page-opcua" class="page" style="display:none;">
<div class="card"> <div class="card">
<h3>OPC_UA Configuration</h3>
<div class="opcua-table-container"> <div class="opcua-table-container">
<table> <table>
<thead> <thead>
@ -445,18 +401,6 @@
</div> </div>
</div> </div>
<div id="opcUa-bottom-actions">
<div class="file-upload-wrapper-protocol"
data-file-type="protocol"
data-expected-filename="protocolConfig.json"
data-upload-url="/setting/protocol"
data-get-url="/setting/get-protocol">
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;">
<button class="select-file-btn-protocol">Load Local Protocol File</button>
</div>
<button class="download-protocol-file">Download Local Protocol File</button>
</div>
</section> </section>
<section id="page-can" class="page" style="display:none;"> <section id="page-can" class="page" style="display:none;">
@ -484,23 +428,10 @@
<button id="can_add_row_btn" type="button">Add Row</button> <button id="can_add_row_btn" type="button">Add Row</button>
</div> </div>
</div> </div>
<div id="can-bottom-actions">
<div class="file-upload-wrapper-protocol"
data-file-type="protocol"
data-expected-filename="protocolConfig.json"
data-upload-url="/setting/protocol"
data-get-url="/setting/get-protocol">
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;">
<button class="select-file-btn-protocol">Load Local Protocol File</button>
</div>
<button class="download-protocol-file">Download Protocol File</button>
</div>
</section> </section>
<section id="page-odometer" class="page" style="display:none;"> <section id="page-odometer" class="page" style="display:none;">
<div class="card"> <div class="card">
<h3>Odometer Configuration</h3>
<div class="odometer-table-container"> <div class="odometer-table-container">
<table> <table>
<thead> <thead>
@ -518,18 +449,6 @@
</table> </table>
</div> </div>
</div> </div>
<div id="odometer-bottom-actions">
<div class="file-upload-wrapper-protocol"
data-file-type="protocol"
data-expected-filename="protocolConfig.json"
data-upload-url="/setting/protocol"
data-get-url="/setting/get-protocol">
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;">
<button class="select-file-btn-protocol">Load Local Protocol File</button>
</div>
<button class="download-protocol-file">Download Protocol File</button>
</div>
</section> </section>

11
src/main/resources/static/js/navigation.js

@ -15,6 +15,8 @@ export function createNavigation() {
const titleElement = byId('page-title'); const titleElement = byId('page-title');
const sidebarButtons = queryAll('.nav-items button'); const sidebarButtons = queryAll('.nav-items button');
const topbarDeviceActions = document.querySelector('.topbar-device-actions');
const topbarProtocolActions = document.querySelector('.topbar-protocol-actions');
const protocolSelect = byId('reg_protocol'); const protocolSelect = byId('reg_protocol');
const canInputSelect = byId('reg_can_input'); const canInputSelect = byId('reg_can_input');
const odometerSelect = byId('OdoMeter'); const odometerSelect = byId('OdoMeter');
@ -32,6 +34,15 @@ export function createNavigation() {
const sourceButton = document.querySelector(`.nav-items button[data-page="${pageName}"]`); const sourceButton = document.querySelector(`.nav-items button[data-page="${pageName}"]`);
titleElement.textContent = sourceButton?.textContent ?? pageName; titleElement.textContent = sourceButton?.textContent ?? pageName;
const isProtocolPage = ['reg', 'modbus', 'opcua', 'can', 'odometer'].includes(pageName);
if (topbarDeviceActions) {
topbarDeviceActions.style.display = isProtocolPage ? 'none' : 'flex';
}
if (topbarProtocolActions) {
topbarProtocolActions.style.display = isProtocolPage ? 'flex' : 'none';
}
} }
function toggleProtocolMenus(protocolValue, canInputValue, odometerValue) { function toggleProtocolMenus(protocolValue, canInputValue, odometerValue) {

19
src/main/resources/static/js/protocol/odometer.js

@ -56,6 +56,25 @@ export function createOdometerController() {
function initialize() { function initialize() {
renderRows(); renderRows();
listElement?.addEventListener('change', (event) => {
if (!event.target.classList.contains('odometer-source-input')) {
return;
}
const row = event.target.closest('tr');
if (!row) {
return;
}
['.odometer-id-input', '.odometer-shift-input', '.odometer-expr-input', '.odometer-mask-input']
.forEach((selector) => {
const input = row.querySelector(selector);
if (input) {
input.value = '';
}
});
});
} }
return { return {

13
src/main/resources/static/js/protocol/protocolValidation.js

@ -86,9 +86,16 @@ export function getRegisterConfigValidationError(data) {
continue; continue;
} }
for (const key of ['odo_speed_source', 'id', 'shift']) { const source = String(item?.odo_speed_source ?? '').trim().toLowerCase();
if (String(item[key] ?? '').trim() === '') { if (!source) {
return `${label} missing '${key}'.`; return `${label} missing 'odo_speed_source'.`;
}
if (source === 'can') {
for (const key of ['id', 'shift']) {
if (String(item[key] ?? '').trim() === '') {
return `${label} missing '${key}'.`;
}
} }
} }
} }

Loading…
Cancel
Save