diff --git a/assets/style.css b/assets/style.css index 9e425b0..b5cf740 100644 --- a/assets/style.css +++ b/assets/style.css @@ -641,6 +641,15 @@ input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; padding .member-emotion { font-size: 17px; line-height: 1; } .member-checkin { font-size: 12.5px; color: var(--text-mute); } .member-del-form { display: inline; margin-left: auto; } +.member-edit { + margin-left: auto; + border: 1px solid var(--border-strong); background: var(--surface); + color: var(--text-soft); font-size: 12.5px; + padding: 3px 10px; border-radius: 999px; cursor: pointer; +} +.member-edit:hover { border-color: var(--accent); color: var(--accent); } +/* 有「编辑」按钮时,移出按钮紧跟其后即可,不再负责把整行推到最右 */ +.member-row .member-edit + .member-del-form { margin-left: 0; } .member-del-form button { border: 0; background: none; color: var(--text-mute); font-size: 15px; cursor: pointer; padding: 2px 6px; border-radius: 6px; @@ -658,18 +667,7 @@ input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; padding .emotion-face { font-size: 16px; line-height: 1; } .emotion-text { font-size: 12.5px; color: var(--text-soft); } -/* ── 13. 花销 ────────────────────────────────────────────────────────────── */ -.expense-summary { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; } -.expense-stat { - flex: 1 1 130px; - padding: 11px 14px; - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); -} -.expense-stat > div:first-child { font-size: 12px; color: var(--text-mute); } -.expense-stat strong { font-size: 16px; } - +/* ── 13. 花销(只记真实花销:上面一笔一笔,下面总计) ────────────────────── */ .expense-list { display: flex; flex-direction: column; gap: 10px; } .expense-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; @@ -683,9 +681,15 @@ input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; padding .expense-desc { font-size: 13px; color: var(--text-soft); margin-top: 2px; } .expense-sub { font-size: 12.5px; color: var(--text-mute); margin-top: 3px; } .expense-amount { font-size: 15px; font-weight: 650; white-space: nowrap; } -.expense-kind { font-size: 11.5px; padding: 1px 7px; border-radius: 999px; font-weight: 550; } -.expense-kind-budget { background: var(--bg-soft); color: var(--text-soft); } -.expense-kind-actual { background: var(--success-soft); color: var(--success); } +.expense-total { + display: flex; align-items: baseline; justify-content: space-between; gap: 10px; + margin-top: 12px; padding: 11px 14px; + background: var(--surface); + border: 1px solid var(--border-strong); + border-radius: var(--radius); + font-size: 13.5px; color: var(--text-soft); +} +.expense-total strong { font-size: 18px; font-weight: 750; color: var(--text); } /* ── 14. 附件与照片 ──────────────────────────────────────────────────────── */ .drop-zone { @@ -1098,3 +1102,60 @@ input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; padding .settings-chip { flex: 1 1 100%; } .inline-row { align-items: stretch; } } + +/* ── 17. 照片灯箱:当前页弹窗看大图,左右箭头/键盘/滑动切换 ────────────────── */ +.lightbox { + position: fixed; inset: 0; z-index: 120; + display: none; align-items: center; justify-content: center; +} +.lightbox.open { display: flex; } +.lightbox-backdrop { position: absolute; inset: 0; background: rgba(10, 13, 20, .88); } +.lightbox-body { + position: relative; z-index: 1; + margin: 0; max-width: min(1100px, 92vw); + display: flex; flex-direction: column; align-items: center; gap: 10px; +} +.lightbox-img { + max-width: min(1100px, 92vw); + max-height: 78vh; + object-fit: contain; + border-radius: var(--radius); + box-shadow: var(--shadow-lg); + background: transparent; +} +.lightbox-cap { + max-width: min(1100px, 92vw); + color: rgba(255, 255, 255, .82); font-size: 13px; + text-align: center; overflow-wrap: anywhere; +} +.lightbox-nav { + position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; + width: 52px; height: 52px; + border: 0; border-radius: 50%; + background: rgba(255, 255, 255, .14); color: #fff; + font-size: 34px; line-height: 1; + cursor: pointer; user-select: none; + display: flex; align-items: center; justify-content: center; +} +.lightbox-nav:hover { background: rgba(255, 255, 255, .28); } +.lightbox-prev { left: max(12px, calc((100vw - min(1100px, 92vw)) / 2 - 72px)); } +.lightbox-next { right: max(12px, calc((100vw - min(1100px, 92vw)) / 2 - 72px)); } +.lightbox-close { + position: absolute; top: 14px; right: 16px; z-index: 2; + width: 42px; height: 42px; + border: 0; border-radius: 50%; + background: rgba(255, 255, 255, .14); color: #fff; + font-size: 26px; line-height: 1; cursor: pointer; +} +.lightbox-close:hover { background: rgba(255, 255, 255, .28); } +.lightbox-count { + position: absolute; top: 24px; left: 20px; z-index: 2; + color: rgba(255, 255, 255, .75); font-size: 13px; + font-variant-numeric: tabular-nums; +} +@media (max-width: 700px) { + .lightbox-nav { width: 46px; height: 46px; font-size: 30px; } + .lightbox-prev { left: 8px; } + .lightbox-next { right: 8px; } + .lightbox-img { max-height: 66vh; } +} diff --git a/src/Expenses.php b/src/Expenses.php index 6ff409c..cc9f024 100644 --- a/src/Expenses.php +++ b/src/Expenses.php @@ -6,8 +6,11 @@ namespace App; /** * 花销与分摊 * - * 表结构:expenses(挂在 trip_nodes 上,is_budget 区分"预算"和"实际") - * + expense_splits(这笔钱按人分摊,谁承担多少) + * 表结构:expenses(挂在 trip_nodes 上)+ expense_splits(这笔钱按人分摊,谁承担多少) + * + * ⚠️ 历史上 expenses 有 is_budget(预算 / 实际)之分,用户明确反馈「预算没有意义」, + * 现在只记真实花销:查询一律 is_budget = 0,表单也不再提供「预算」选项。 + * 列本身保留(老数据还在库里,不至于丢),但 UI 与汇总都不再出现预算概念。 */ class Expenses { @@ -23,7 +26,7 @@ class Expenses return ['CNY' => '人民币 ¥', 'JPY' => '日元 ¥', 'USD' => '美元 $', 'EUR' => '欧元 €', 'HKD' => '港币 HK$']; } - /** 某个节点下的花销,带付款人和分摊明细 */ + /** 某个节点下的花销(只看真实花销),带付款人和分摊明细 */ public static function forNode(int $nodeId): array { $db = Database::get(); @@ -31,8 +34,8 @@ class Expenses SELECT e.*, u.name AS payer_name, u.avatar AS payer_avatar FROM expenses e LEFT JOIN users u ON u.id = e.payer_user_id - WHERE e.trip_node_id = ? - ORDER BY e.is_budget ASC, e.created_at ASC, e.id ASC'); + WHERE e.trip_node_id = ? AND e.is_budget = 0 + ORDER BY e.created_at ASC, e.id ASC'); $stmt->execute([$nodeId]); $rows = $stmt->fetchAll(); if (empty($rows)) return []; @@ -56,41 +59,32 @@ class Expenses return $rows; } - /** 活动下的花销合计(预算 / 实际),以及各节点的小计 */ + /** 活动下的真实花销总计,以及各节点的小计 */ public static function summaryForActivity(int $activityId): array { $db = Database::get(); $stmt = $db->prepare(' - SELECT tn.id AS node_id, e.is_budget, SUM(e.total_amount) AS amount, COUNT(*) AS cnt + SELECT tn.id AS node_id, SUM(e.total_amount) AS amount, COUNT(*) AS cnt FROM expenses e JOIN trip_nodes tn ON tn.id = e.trip_node_id - WHERE tn.activity_id = ? - GROUP BY tn.id, e.is_budget'); + WHERE tn.activity_id = ? AND e.is_budget = 0 + GROUP BY tn.id'); $stmt->execute([$activityId]); $byNode = []; - $budgetTotal = 0.0; - $actualTotal = 0.0; + $total = 0.0; $count = 0; foreach ($stmt->fetchAll() as $row) { - $nodeId = (int)$row['node_id']; - $isBudget = (int)$row['is_budget'] === 1; $amount = (float)$row['amount']; - $byNode[$nodeId][$isBudget ? 'budget' : 'actual'] = $amount; - if ($isBudget) { - $budgetTotal += $amount; - } else { - $actualTotal += $amount; - } + $byNode[(int)$row['node_id']] = $amount; + $total += $amount; $count += (int)$row['cnt']; } return [ - 'budget' => $budgetTotal, - 'actual' => $actualTotal, - 'count' => $count, - 'by_node' => $byNode, - 'diff' => $actualTotal - $budgetTotal, + 'total' => $total, + 'count' => $count, + 'by_node' => $byNode, ]; } @@ -191,23 +185,13 @@ class Expenses return true; } - /** 某个节点的花销合计 */ + /** 某个节点的真实花销合计 */ public static function summaryForNode(int $nodeId): array { - $stmt = Database::get()->prepare('SELECT is_budget, SUM(total_amount) AS amount, COUNT(*) AS cnt - FROM expenses WHERE trip_node_id = ? GROUP BY is_budget'); + $stmt = Database::get()->prepare('SELECT COALESCE(SUM(total_amount), 0) AS amount, COUNT(*) AS cnt + FROM expenses WHERE trip_node_id = ? AND is_budget = 0'); $stmt->execute([$nodeId]); - $budget = 0.0; - $actual = 0.0; - $count = 0; - foreach ($stmt->fetchAll() as $row) { - if ((int)$row['is_budget'] === 1) { - $budget = (float)$row['amount']; - } else { - $actual = (float)$row['amount']; - } - $count += (int)$row['cnt']; - } - return ['budget' => $budget, 'actual' => $actual, 'count' => $count, 'diff' => $actual - $budget]; + $row = $stmt->fetch(); + return ['total' => (float)$row['amount'], 'count' => (int)$row['cnt']]; } } diff --git a/templates/_lightbox.php b/templates/_lightbox.php new file mode 100644 index 0000000..2f2cd4d --- /dev/null +++ b/templates/_lightbox.php @@ -0,0 +1,125 @@ +" data-cap="<说明文字>">… + * (没有 data-cap 时回落到 title / alt / href) + * 2. 在页面末尾 require 本模板(一份就够,多页共用同一份标记与脚本) + * + * 依赖外部变量:无。 + */ +?> +
+ + diff --git a/templates/activities/show.php b/templates/activities/show.php index f48736e..11b615d 100644 --- a/templates/activities/show.php +++ b/templates/activities/show.php @@ -22,16 +22,11 @@ - 0.0, 'actual' => 0.0, 'count' => 0, 'diff' => 0.0]; ?> + 0.0, 'count' => 0]; ?> 0): ?>还没有记账
@@ -53,11 +38,6 @@ $money = static function (float $v): string {