205 lines
8.6 KiB
HTML
205 lines
8.6 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Проводка по дому — калькулятор</title>
|
||
<style>
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{font:17px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;background:#f0f2f5;color:#0F1218;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px}
|
||
.card{background:#fff;border-radius:20px;padding:48px;max-width:520px;width:100%;box-shadow:0 4px 24px rgba(0,0,0,.06)}
|
||
h1{font-size:26px;font-weight:800;margin-bottom:4px}
|
||
.sub{color:#5B6573;font-size:15px;margin-bottom:24px}
|
||
.settings{background:#f6f8fa;border-radius:12px;padding:20px;margin-bottom:24px}
|
||
.settings-row{display:flex;gap:12px;margin-bottom:12px}
|
||
.settings-row:last-child{margin-bottom:0}
|
||
.settings-row label{flex:1;font-size:14px;font-weight:600;color:#5B6573}
|
||
.settings-row input{width:100%;padding:10px 14px;font-size:16px;font-weight:600;border:2px solid #d0d5dd;border-radius:8px;outline:none;text-align:center;transition:.15s}
|
||
.settings-row input:focus{border-color:#0F1218}
|
||
label[for=point]{display:block;font-weight:600;font-size:15px;margin-bottom:8px}
|
||
input#point{width:100%;padding:16px 20px;font-size:24px;font-weight:700;border:2px solid #d0d5dd;border-radius:12px;outline:none;transition:.15s;text-align:center}
|
||
input#point:focus{border-color:#0F1218}
|
||
input#point::placeholder{font-weight:400;color:#9aa3b2;font-size:17px}
|
||
button{width:100%;padding:16px;background:#0F1218;color:#fff;border:none;border-radius:12px;font-size:18px;font-weight:700;cursor:pointer;margin-top:16px;transition:.15s}
|
||
button:hover{background:#2a2f3a}
|
||
button:active{transform:scale(.98)}
|
||
.result{padding:24px;background:#f0f2f5;border-radius:12px;margin-top:24px;text-align:center;display:none}
|
||
.result.show{display:block}
|
||
.result .big{font-size:36px;font-weight:800;color:#0F1218}
|
||
.result .big span{color:#0088cc}
|
||
.result .detail{font-size:14px;color:#5B6573;margin-top:8px}
|
||
.error{color:#e74c3c;font-size:14px;margin-top:8px;display:none}
|
||
.error.show{display:block}
|
||
.history{margin-top:32px;padding-top:24px;border-top:2px solid #e8eaed;display:none}
|
||
.history.show{display:block}
|
||
.history h3{font-size:15px;color:#5B6573;font-weight:600;margin-bottom:12px}
|
||
.history-item{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-radius:8px;cursor:pointer;transition:.1s;font-size:15px}
|
||
.history-item:hover{background:#f0f2f5}
|
||
.history-item .h-num{font-weight:700}
|
||
.history-item .h-result{color:#0088cc;font-weight:600}
|
||
.history-item .h-copy{color:#9aa3b2;font-size:13px;cursor:pointer;padding:4px 8px;border-radius:4px}
|
||
.history-item .h-copy:hover{background:#d0d5dd;color:#0F1218}
|
||
.room-list{margin-top:20px;font-size:14px}
|
||
.room-list summary{font-weight:600;color:#5B6573;cursor:pointer;padding:6px 0}
|
||
.room-list .rrow{display:flex;justify-content:space-between;padding:4px 8px;border-radius:4px;font-size:13px}
|
||
.room-list .rrow:nth-child(odd){background:#f8f9fa}
|
||
@media(max-width:480px){.card{padding:24px}.settings-row{flex-direction:column;gap:8px}h1{font-size:22px}input#point{font-size:20px}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="card">
|
||
<h1>🏠 Проводка по дому</h1>
|
||
<p class="sub" id="subtitle">Комнаты и точки: расстановка</p>
|
||
|
||
<div class="settings">
|
||
<div class="settings-row">
|
||
<label>Комнат <input type="number" id="rooms" min="1" max="99" onchange="applySettings()"></label>
|
||
<label>Точек на комнату <input type="number" id="perRoom" min="1" max="99" onchange="applySettings()"></label>
|
||
</div>
|
||
</div>
|
||
|
||
<label for="point">Введи номер точки (розетка/выключатель/свет)</label>
|
||
<input type="number" id="point" min="1" placeholder="например, 45" autofocus>
|
||
<div class="error" id="error">Введи число в диапазоне</div>
|
||
<button onclick="calc()">Рассчитать</button>
|
||
|
||
<div class="result" id="result">
|
||
<div class="big" id="output"></div>
|
||
<div class="detail" id="detail"></div>
|
||
</div>
|
||
|
||
<div class="room-list" id="roomList"></div>
|
||
|
||
<div class="history" id="history">
|
||
<h3>📋 История</h3>
|
||
<div id="historyList"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const MAX_HISTORY=50;
|
||
let PER_ROOM=6;
|
||
let TOTAL_ROOMS=10;
|
||
let TOTAL_POINTS=TOTAL_ROOMS*PER_ROOM;
|
||
let history=JSON.parse(localStorage.getItem('hwHistory')||'[]');
|
||
|
||
const roomNames=[
|
||
'Прихожая','Коридор','Гостиная','Кухня','Спальня','Детская','Ванная','Туалет',
|
||
'Кладовая','Балкон','Кабинет','Гардеробная','Прачечная','Котельная','Холл','Столовая',
|
||
'Гостевая','Спортзал','Бильярдная','Винный погреб'
|
||
];
|
||
|
||
function loadSettings(){
|
||
const r=localStorage.getItem('hwRooms');
|
||
const p=localStorage.getItem('hwPerRoom');
|
||
if(r)document.getElementById('rooms').value=TOTAL_ROOMS=parseInt(r);
|
||
if(p)document.getElementById('perRoom').value=PER_ROOM=parseInt(p);
|
||
TOTAL_POINTS=TOTAL_ROOMS*PER_ROOM;
|
||
}
|
||
function saveSettings(){
|
||
localStorage.setItem('hwRooms',TOTAL_ROOMS);
|
||
localStorage.setItem('hwPerRoom',PER_ROOM);
|
||
}
|
||
function updateUI(){
|
||
document.getElementById('point').max=TOTAL_POINTS;
|
||
document.getElementById('point').placeholder=`1–${TOTAL_POINTS}`;
|
||
document.getElementById('subtitle').textContent=`${TOTAL_ROOMS} комнат${TOTAL_ROOMS%10===1?'а':'ы'} × ${PER_ROOM} точек = 1–${TOTAL_POINTS}`;
|
||
}
|
||
function applySettings(){
|
||
const r=parseInt(document.getElementById('rooms').value);
|
||
const p=parseInt(document.getElementById('perRoom').value);
|
||
if(!r||r<1||!p||p<1)return;
|
||
TOTAL_ROOMS=r;
|
||
PER_ROOM=p;
|
||
TOTAL_POINTS=TOTAL_ROOMS*PER_ROOM;
|
||
saveSettings();
|
||
updateUI();
|
||
document.getElementById('result').classList.remove('show');
|
||
renderRoomList();
|
||
}
|
||
|
||
function getRoomName(i){
|
||
return roomNames[i%roomNames.length]+(i>=roomNames.length?' '+(Math.floor(i/roomNames.length)+1):'');
|
||
}
|
||
|
||
function calc(){
|
||
const input=document.getElementById('point');
|
||
const error=document.getElementById('error');
|
||
const result=document.getElementById('result');
|
||
const output=document.getElementById('output');
|
||
const detail=document.getElementById('detail');
|
||
const val=parseInt(input.value);
|
||
|
||
error.classList.remove('show');
|
||
result.classList.remove('show');
|
||
|
||
if(!val||val<1||val>TOTAL_POINTS){
|
||
error.textContent=`Введи число от 1 до ${TOTAL_POINTS}`;
|
||
error.classList.add('show');
|
||
return;
|
||
}
|
||
|
||
const roomIdx=Math.floor((val-1)/PER_ROOM);
|
||
const pointInRoom=((val-1)%PER_ROOM)+1;
|
||
const roomName=getRoomName(roomIdx);
|
||
const firstPoint=roomIdx*PER_ROOM+1;
|
||
const lastPoint=(roomIdx+1)*PER_ROOM;
|
||
|
||
output.innerHTML=`${roomName} <span>— точка ${pointInRoom}</span>`;
|
||
detail.textContent=`№${val}: точки ${firstPoint}–${lastPoint}`;
|
||
result.classList.add('show');
|
||
|
||
addHistory(val,roomName,pointInRoom);
|
||
}
|
||
|
||
function addHistory(num,room,point){
|
||
const entry={num,room,point,time:Date.now()};
|
||
history.unshift(entry);
|
||
if(history.length>MAX_HISTORY)history=history.slice(0,MAX_HISTORY);
|
||
localStorage.setItem('hwHistory',JSON.stringify(history));
|
||
renderHistory();
|
||
}
|
||
function renderHistory(){
|
||
const el=document.getElementById('history');
|
||
const list=document.getElementById('historyList');
|
||
if(!history.length){el.classList.remove('show');return}
|
||
el.classList.add('show');
|
||
list.innerHTML=history.map((h,i)=>`
|
||
<div class="history-item" onclick="useHistory(${i})">
|
||
<span class="h-num">№${h.num}</span>
|
||
<span class="h-result">${h.room} — т.${h.point}</span>
|
||
<span class="h-copy" onclick="event.stopPropagation();copyResult(${i})" title="Копировать">📋</span>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
function useHistory(i){
|
||
document.getElementById('point').value=history[i].num;
|
||
calc();
|
||
}
|
||
function copyResult(i){
|
||
const h=history[i];
|
||
const text=`№${h.num} → ${h.room} — точка ${h.point}`;
|
||
navigator.clipboard.writeText(text).catch(()=>{});
|
||
}
|
||
|
||
function renderRoomList(){
|
||
const el=document.getElementById('roomList');
|
||
let html='<details><summary>📋 Все комнаты</summary>';
|
||
for(let i=0;i<TOTAL_ROOMS;i++){
|
||
const first=i*PER_ROOM+1;
|
||
const last=(i+1)*PER_ROOM;
|
||
html+=`<div class="rrow"><span>${getRoomName(i)}</span><span>точки ${first}–${last}</span></div>`;
|
||
}
|
||
html+='</details>';
|
||
el.innerHTML=html;
|
||
}
|
||
|
||
document.getElementById('point').addEventListener('keydown',e=>{if(e.key==='Enter')calc()});
|
||
loadSettings();
|
||
updateUI();
|
||
renderRoomList();
|
||
renderHistory();
|
||
</script>
|
||
</body>
|
||
</html>
|