SOURCE

console 命令行工具 X clear

                    
>
console
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>随手记</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;font-family: -apple-system, system-ui}
body{background-color:#f0f5f2;padding:16px}

/*顶部绿色汇总卡片*/
.top-card{
  background:linear-gradient(180deg,#248257,#1b7049);
  border-radius:22px;
  color:white;
  text-align:center;
  padding:30px 20px;
  margin-bottom:24px;
}
.top-month{
  font-size:17px;
  opacity:0.9;
  margin-bottom:8px;
}
.top-label{
  font-size:18px;
  opacity:0.9;
  margin-bottom:8px;
}
.top-money{
  font-size:64px;
  font-weight:bold;
  line-height:1;
  margin-bottom:16px;
}
.top-row{
  display:flex;
  justify-content:center;
  gap:24px;
  font-size:16px;
  opacity:0.92;
}

/* 支出收入按钮 */
.btn-wrap{
  display:flex;
  gap:20px;
  margin-bottom:24px;
}
.btn-item{
  text-align:center;
}
.btn-circle-red{
  width:72px;height:72px;
  border-radius:50%;
  background:#f8d9d9;
  color:#d64545;
  border:none;
  font-size:30px;
  margin-bottom:6px;
}
.btn-circle-green{
  width:72px;height:72px;
  border-radius:50%;
  background:#d9f0e4;
  color:#248257;
  border:none;
  font-size:30px;
  margin-bottom:6px;
}
.btn-text{
  font-size:15px;color:#444;
}

/*每日结算区域*/
.section-title{
  display:flex;
  justify-content:space-between;
  color:#555;
  font-size:16px;
  margin-bottom:12px;
}
.day-block{
  background:#fff;
  border-radius:18px;
  padding:20px;
  margin-bottom:16px;
  box-shadow:0 2px 10px rgba(0,0,0,0.06);
}
.day-header{
  display:flex;
  justify-content:space-between;
  margin-bottom:18px;
}
.day-date{
  font-size:18px;font-weight:500;
  color:#333;
}
.day-balance{
  font-size:18px;font-weight:bold;color:#248257;
}
.day-sub{
  color:#666;font-size:15px;margin-top:4px;
}

/*单条账单项目*/
.bill-item{
  display:flex;
  justify-content:space-between;
  align-items:center;
  padding:12px 0;
  border-bottom:1px solid #eee;
}
.bill-item:last-child{border-bottom:none;}
.bill-left{
  display:flex;
  align-items:center;
  gap:12px;
}
.bill-icon-red{
  width:44px;height:44px;border-radius:12px;background:#f8d9d9;color:#d64545;
  display:grid;place-items:center;font-size:20px;
}
.bill-icon-green{
  width:44px;height:44px;border-radius:12px;background:#d9f0e4;color:#248257;
  display:grid;place-items:center;font-size:20px;
}
.bill-info .note{font-size:17px;color:#222}
.bill-info .time{font-size:14px;color:#777;margin-top:3px}
.bill-right{
  display:flex;align-items:center;gap:10px;
}
.money-plus{color:#248257;font-size:19px;font-weight:500}
.money-minus{color:#d64545;font-size:19px;font-weight:500}
.del-btn{
  background:#e53935;color:white;border:none;border-radius:6px;padding:4px 8px;font-size:13px;
}

/*底部加号按钮*/
.add-float{
  position:fixed;
  left:50%;
  transform:translateX(-50%);
  bottom:32px;
  width:70px;height:70px;
  border-radius:50%;
  background:#248257;
  color:#fff;
  border:none;
  font-size:34px;
  box-shadow:0 4px 12px rgba(36,130,87,0.35);
}

/*弹出录入面板*/
.pop-mask{
  position:fixed;inset:0;background:rgba(0,0,0,0.4);
  display:none;align-items:flex-end;
}
.pop-box{
  width:100%;background:#fff;padding:24px;border-radius:20px 20px 0 0;
}
.pop-type-title{
  text-align:center;font-size:20px;margin-bottom:16px;
}
.input-money{
  width:100%;font-size:36px;text-align:right;border:none;outline:none;padding:10px 0;
  border-bottom:1px solid #ddd;margin-bottom:16px;
}
.input-note{
  width:100%;font-size:18px;border:1px solid #ddd;border-radius:10px;padding:12px;margin-bottom:18px;
}
.keyboard{
  display:grid;grid-template-columns:repeat(3,1fr);gap:10px;
}
.key{
  padding:16px;font-size:22px;border:none;border-radius:10px;background:#eee;
}
.key-save{background:#248257;color:#fff}
.key-cancel{background:#999;color:#fff}
</style>
</head>
<body>
<!--顶部绿色卡片-->
<div class="top-card">
  <div class="top-month" id="topMonth">10月</div>
  <div class="top-label">本月支出</div>
  <div class="top-money" id="monthOut">0.00</div>
  <div class="top-row">
    <span>收入 ¥<span id="monthIn">0.00</span></span>
    <span>结余 ¥<span id="monthBalance">0.00</span></span>
    <span id="billCount">0笔</span>
  </div>
</div>

<!--支出 收入圆形按钮-->
<div class="btn-wrap">
  <div class="btn-item">
    <button class="btn-circle-red" onclick="openPop('out')">+</button>
    <div class="btn-text">支出</div>
  </div>
  <div class="btn-item">
    <button class="btn-circle-green" onclick="openPop('in')">⤒</button>
    <div class="btn-text">收入</div>
  </div>
</div>

<div class="section-title">
  <span>每日结算</span>
  <span>今日支出 ¥<span id="todayOut">0.00</span></span>
</div>

<div id="listWrap"></div>

<!--底部悬浮加号-->
<button class="add-float" onclick="openPop('out')">+</button>

<!--弹窗录入面板-->
<div class="pop-mask" id="pop">
  <div class="pop-box">
    <div class="pop-type-title" id="popTitle">支出</div>
    <input class="input-money" id="moneyInput" value="0">
    <input class="input-note" id="noteInput" placeholder="备注(餐、工资等)">
    <div class="keyboard">
      <button class="key" onclick="inputNum('1')">1</button>
      <button class="key" onclick="inputNum('2')">2</button>
      <button class="key" onclick="inputNum('3')">3</button>
      <button class="key" onclick="inputNum('4')">4</button>
      <button class="key" onclick="inputNum('5')">5</button>
      <button class="key" onclick="inputNum('6')">6</button>
      <button class="key" onclick="inputNum('7')">7</button>
      <button class="key" onclick="inputNum('8')">8</button>
      <button class="key" onclick="inputNum('9')">9</button>
      <button class="key" onclick="inputNum('.')">.</button>
      <button class="key" onclick="inputNum('0')">0</button>
      <button class="key key-cancel" onclick="closePop()">取消</button>
      <button class="key" onclick="delOne()">⌫</button>
      <button class="key key-save" onclick="saveBill()">保存</button>
    </div>
  </div>
</div>

<script>
let bills = JSON.parse(localStorage.getItem('billData')) || [];
let currentType = 'out';
render();

function openPop(type){
  currentType = type;
  document.getElementById('popTitle').innerText = type==='out'?'支出':'收入';
  document.getElementById('moneyInput').value = '0';
  document.getElementById('noteInput').value = '';
  document.getElementById('pop').style.display='flex';
}
function closePop(){
  document.getElementById('pop').style.display='none';
}
function inputNum(v){
  let val = document.getElementById('moneyInput').value;
  if(val === '0' && v!=='.') val = '';
  val += v;
  document.getElementById('moneyInput').value = val;
}
function delOne(){
  let val = document.getElementById('moneyInput').value;
  val = val.slice(0,-1);
  if(val === '' || val === '.') val = '0';
  document.getElementById('moneyInput').value = val;
}

function saveBill(){
  let money = parseFloat(document.getElementById('moneyInput').value);
  let note = document.getElementById('noteInput').value.trim();
  if(isNaN(money) || money <=0) return alert('金额必须大于0');
  let now = new Date();
  bills.push({
    id:Date.now(),
    type:currentType,
    money:money,
    note: note,
    time: now.toISOString(),
    timeDisplay: `${now.getHours()}:${String(now.getMinutes()).padStart(2,'0')}`
  });
  saveStorage();
  render();
  closePop();
}
function deleteBill(id){
  if(confirm('确定删除这条记录?')){
    bills = bills.filter(item=>item.id!==id);
    saveStorage();
    render();
  }
}
function saveStorage(){
  localStorage.setItem('billData', JSON.stringify(bills));
}

function render(){
  const nowDate = new Date();
  const currentMonth = nowDate.getMonth()+1;
  const currentYear = nowDate.getFullYear();

  //本月统计
  let monthIn=0,monthOut=0;
  let todayOut=0;
  bills.forEach(b=>{
    let d = new Date(b.time);
    //判断是否本月
    if(d.getFullYear()===currentYear && d.getMonth()+1 === currentMonth){
      if(b.type==='in') monthIn += b.money;
      else monthOut += b.money;
    }
    //今日支出
    if(d.toDateString() === nowDate.toDateString()){
      if(b.type==='out') todayOut += b.money;
    }
  })
  const monthBal = monthIn - monthOut;
  const totalCount = bills.length;

  //更新顶部绿色卡片
  document.getElementById('topMonth').innerText = `${currentMonth}月`;
  document.getElementById('monthOut').innerText = monthOut.toFixed(2);
  document.getElementById('monthIn').innerText = monthIn.toFixed(2);
  document.getElementById('monthBalance').innerText = monthBal.toFixed(2);
  document.getElementById('billCount').innerText = `${totalCount}笔`;
  document.getElementById('todayOut').innerText = todayOut.toFixed(2);

  //按日期分组
  const group = {};
  bills.forEach(b=>{
    const d = new Date(b.time);
    const dateStr = `${d.getFullYear()}/${d.getMonth()+1}/${d.getDate()}`;
    if(!group[dateStr]) group[dateStr] = {list:[],in:0,out:0};
    group[dateStr].list.push(b);
    if(b.type==='in') group[dateStr].in += b.money;
    else group[dateStr].out += b.money;
  })

  let html = '';
  //日期倒序,最新放上面
  Object.keys(group).sort((a,b)=>new Date(b)-new Date(a)).forEach(date=>{
    const g = group[date];
    const dayBal = g.in - g.out;
    html += `
    <div class="day-block">
      <div class="day-header">
        <div>
          <div class="day-date">${date}</div>
          <div class="day-sub">支出¥${g.out.toFixed(2)} &nbsp;收入¥${g.in.toFixed(2)}</div>
        </div>
        <div class="day-balance">结余 ${dayBal>=0?'+':''}${dayBal.toFixed(2)}</div>
      </div>
    `;
    g.list.forEach(item=>{
      const isIn = item.type === 'in';
      const iconClass = isIn ? 'bill-icon-green' : 'bill-icon-red';
      const moneyClass = isIn ? 'money-plus' : 'money-minus';
      const sign = isIn ? '+' : '-';
      html += `
      <div class="bill-item">
        <div class="bill-left">
          <div class="${iconClass}">${isIn ? '⤒' : '+'}</div>
          <div class="bill-info">
            <div class="note">${item.note||(isIn?'收入':'支出')}</div>
            <div class="time">${isIn?'收入':'支出'} · ${item.timeDisplay}</div>
          </div>
        </div>
        <div class="bill-right">
          <span class="${moneyClass}">${sign}${item.money.toFixed(2)}</span>
          <button class="del-btn" onclick="deleteBill(${item.id})">删除</button>
        </div>
      </div>
      `
    })
    html += '</div>'
  })
  document.getElementById('listWrap').innerHTML = html;
}
</script>
</body>
</html>