Files
TripLog/assets/style.css
T
laoyang a79956758b 补齐未接入代码的功能域:参与人 / 评论 / 花销与分摊
数据库里早就建好了 comments、node_members + role_tags、expenses +
expense_splits 三组表,但一直没有代码接入。本次把这三块连上业务,
并新增「节点详情页」作为聚合入口。

新增
- src/Access.php          统一归属校验。此前每个控制器各写各的 family_id
                          条件,容易漏;现在 activity()/node()/target() 一处收敛,
                          requireAdmin() 统一管理员判断。
- src/Comments.php        两级平铺:parent_id 只允许指向顶层,写「回复的回复」
                          时规范化回顶层;删顶层会连同回复一起删。
- src/Members.php         参与人 + 角色标签 + 情绪 + 签到。情绪目录 17 项,
                          全部选用 Emoji 11.0(2018)及以前的码位,避免旧系统
                          字体缺字形显示成空白方块。
- src/Expenses.php        花销与分摊。分摊按「分」为单位 intdiv 平均,
                          余数归最后一人,保证 SUM(share_amount) = total_amount。
- templates/nodes/show.php 节点详情页:基本信息 + 参与人 + 花销 + 讨论 + 附件入口。
- 角色标签管理、评论区、参与人区、花销区各自的模板与控制器。

修复
- 越权:往别人活动的节点上插节点、把别家用户加成参与人,都只校验了
  「节点存在」,没校验「用户与节点同家庭」。现统一走 Access。
- 删活动时节点附件文件删不掉:附件在 /uploads/attachments/ 下,却调了
  只认 /uploads/covers/ 的删除函数,判定不通过 → 文件永久残留。改为
  统一的 deleteUploadFile(),两个目录都认。
- 删有引用记录的用户会 500(activities.created_by / attachments.uploader_id /
  comments.author_id / expenses.payer_user_id 这几处没有级联,也没做前置检查)。

其他
- 活动/节点列表补上附件、评论、参与人、花销四个计数(一次 GROUP BY 查完)。
- 首页布局新增一条迁移易踩的规避:新列上的索引必须排在 ALTER TABLE 之后。
- 新增 .gitignore:config.php(含数据库口令)与 uploads/ 下用户上传的
  头像封面不入库,只保留 uploads/.htaccess 那条禁止执行脚本的规则。
- 新增 config.example.php 作为新环境的配置模板。

本批不改数据库结构,不需要任何数据迁移。

本地与线上验证
- php -l 全绿(PHP 8.4 本地 / 线上 7.4.30 与 8.1.7 双版本)。
- 模板渲染检查 180 项全通过。
- 线上端到端 153 项全通过:登录、活动增删改查、节点流转、
  参与人/角色标签/情绪/签到、评论、花销与分摊、权限、级联清理、
  全站页面无 PHP 报错泄漏、跑完基线逐表核对且既有 id 集合逐字未变。
- 顺带写了 trip_sql_column_check.py:拿真实库表结构静态校验全项目 SQL 的
  列名引用。本次就是靠它之外的 e2e 抓到 travel_modes 并没有 line_name
  (该列属于 trip_nodes),一个列名写错导致节点详情页整页 Fatal。
2026-09-22 22:03:08 +08:00

969 lines
24 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
* { box-sizing: border-box; }
body {
margin: 0;
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif;
background: #f5f5f7;
color: #1d1d1f;
line-height: 1.6;
}
a { color: #007aff; text-decoration: none; }
a:hover { text-decoration: underline; }
.topbar {
background: #fff;
border-bottom: 1px solid #e5e5e7;
padding: 14px 24px;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 10;
}
.brand { font-weight: 600; font-size: 17px; color: #1d1d1f; }
.topbar nav { display: flex; gap: 16px; align-items: center; }
.container { max-width: 1100px; margin: 0 auto; padding: 24px; }
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 24px;
gap: 16px;
}
.page-header h1 { margin: 0 0 4px; font-size: 24px; }
.page-header .muted { margin: 0; }
.btn, .btn-primary {
display: inline-block;
padding: 8px 16px;
border-radius: 8px;
border: 1px solid #d2d2d7;
background: #fff;
color: #1d1d1f;
font-size: 14px;
cursor: pointer;
transition: 0.15s;
line-height: 1.4;
}
.btn:hover, .btn-primary:hover { background: #f0f0f2; text-decoration: none; }
.btn-primary { background: #007aff; color: #fff; border-color: #007aff; }
.btn-primary:hover { background: #0066d6; }
.alert {
background: #fff3cd;
border: 1px solid #ffe08a;
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 16px;
color: #664d03;
}
.empty {
text-align: center;
padding: 60px 20px;
background: #fff;
border-radius: 12px;
border: 1px solid #e5e5e7;
}
.empty p { margin: 0 0 16px; color: #86868b; }
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 16px;
}
.card {
display: block;
background: #fff;
border-radius: 12px;
border: 1px solid #e5e5e7;
overflow: hidden;
color: inherit;
transition: 0.15s;
}
.card:hover { text-decoration: none; transform: translateY(-2px); box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
.card-cover {
width: 100%;
height: 140px;
object-fit: cover;
display: block;
background: #e5e5e7;
}
.card-cover-placeholder {
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
color: #c7c7cc;
}
.card-body { padding: 14px 16px; }
.card-body h3 { margin: 0 0 6px; font-size: 16px; }
.card-body .muted { color: #86868b; font-size: 13px; margin: 0 0 10px; }
.card .meta { display: flex; justify-content: space-between; font-size: 12px; color: #86868b; }
.timeline-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
@media (max-width: 720px) {
.timeline-grid { grid-template-columns: 1fr; }
}
.timeline-col h2 { font-size: 16px; margin: 0 0 12px; color: #555; }
.node-card {
background: #fff;
border-radius: 10px;
border: 1px solid #e5e5e7;
padding: 12px 14px;
margin-bottom: 10px;
}
.node-plan { border-left: 4px solid #c7c7cc; }
.node-reality { border-left: 4px solid #007aff; }
.node-head { display: flex; align-items: center; gap: 6px; font-size: 13px; color: #555; margin-bottom: 4px; }
.node-icon { font-size: 16px; }
.node-time { font-size: 12px; color: #86868b; margin-bottom: 4px; }
.node-title { font-weight: 600; font-size: 15px; }
.node-loc { font-size: 13px; color: #555; margin-top: 4px; }
.tag-temp {
background: #fff3cd;
color: #664d03;
font-size: 11px;
padding: 1px 6px;
border-radius: 4px;
margin-left: auto;
}
.form { background: #fff; border-radius: 12px; padding: 24px; border: 1px solid #e5e5e7; max-width: 720px; }
.form-row { margin-bottom: 18px; }
.form-row label { display: block; font-size: 13px; color: #555; margin-bottom: 6px; font-weight: 500; }
.form-row input[type=text],
.form-row input[type=password],
.form-row input[type=datetime-local],
.form-row textarea,
.form-row select {
width: 100%;
padding: 9px 12px;
border: 1px solid #d2d2d7;
border-radius: 8px;
font-size: 14px;
font-family: inherit;
background: #fff;
}
.form-row textarea { resize: vertical; }
.form-row input:focus, .form-row textarea:focus, .form-row select:focus {
outline: none;
border-color: #007aff;
box-shadow: 0 0 0 3px rgba(0,122,255,0.15);
}
.form-row.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 600px) {
.form-row.two-col { grid-template-columns: 1fr; }
}
.form-actions { display: flex; justify-content: flex-end; gap: 10px; padding-top: 8px; }
.footer { text-align: center; color: #86868b; font-size: 12px; padding: 40px 20px 20px; }
.muted { color: #86868b; }
/* ====== 登录页 ====== */
.container-guest { display: flex; justify-content: center; align-items: center; min-height: 100vh; }
.login-wrapper { width: 100%; max-width: 380px; padding: 24px; }
.login-form {
background: #fff;
border-radius: 16px;
padding: 32px 28px;
border: 1px solid #e5e5e7;
box-shadow: 0 4px 24px rgba(0,0,0,0.04);
}
.login-form h1 { margin: 0 0 4px; font-size: 22px; text-align: center; }
.login-form .muted { text-align: center; margin: 0 0 24px; font-size: 13px; }
.user-info {
padding: 4px 10px;
background: #f0f0f2;
border-radius: 6px;
font-size: 13px;
color: #555;
}
.logout-link { font-size: 13px; color: #86868b; }
.logout-link:hover { color: #ff3b30; }
/* ====== 数据表格 ====== */
.data-table {
width: 100%;
background: #fff;
border-radius: 12px;
border: 1px solid #e5e5e7;
border-collapse: separate;
border-spacing: 0;
overflow: hidden;
}
.data-table th, .data-table td {
padding: 12px 16px;
text-align: left;
font-size: 14px;
border-bottom: 1px solid #f0f0f2;
}
.data-table th {
background: #fafafa;
font-weight: 500;
color: #555;
font-size: 13px;
}
.data-table tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover { background: #fafafa; }
.user-cell { display: flex; align-items: center; gap: 8px; }
.avatar-sm {
width: 28px; height: 28px;
border-radius: 50%;
object-fit: cover;
display: inline-flex;
align-items: center;
justify-content: center;
}
.avatar-placeholder {
background: #e5e5e7;
color: #86868b;
font-size: 13px;
font-weight: 600;
}
.role-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
.role-admin { background: #ffe5e5; color: #c0392b; }
.role-user { background: #e0f0ff; color: #007aff; }
.role-guest { background: #f0f0f2; color: #86868b; }
.status-active { color: #34c759; font-size: 12px; }
.status-inactive { color: #86868b; font-size: 12px; }
.tag-self {
background: #007aff;
color: #fff;
font-size: 10px;
padding: 1px 5px;
border-radius: 3px;
}
.btn-sm {
display: inline-block;
padding: 4px 10px;
font-size: 12px;
border-radius: 6px;
border: 1px solid #d2d2d7;
background: #fff;
color: #1d1d1f;
cursor: pointer;
text-decoration: none;
}
.btn-sm:hover { background: #f0f0f2; text-decoration: none; }
.btn-danger { color: #ff3b30; border-color: #ffd0cc; }
.btn-danger:hover { background: #ffe5e5; }
/* ====== 头像上传 ====== */
.avatar-upload {
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
.avatar-preview {
width: 88px;
height: 88px;
border-radius: 50%;
background: #f0f0f2;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
border: 2px dashed #d2d2d7;
flex-shrink: 0;
}
.avatar-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.avatar-placeholder {
font-size: 32px;
color: #c7c7cc;
font-weight: 300;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.avatar-upload-actions {
display: flex;
flex-direction: column;
gap: 8px;
}
/* ====== 裁剪模态框 ====== */
.modal {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-backdrop {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.65);
}
.modal-content {
position: relative;
background: #fff;
border-radius: 14px;
padding: 24px;
width: 90%;
max-width: 520px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
.modal-content h2 {
margin: 0 0 16px;
font-size: 18px;
}
.crop-container {
width: 100%;
height: 360px;
background: #f0f0f2;
border-radius: 8px;
overflow: hidden;
margin-bottom: 16px;
}
.crop-container img {
display: block;
max-width: 100%;
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
/* ====== 活动封面上传 ====== */
.cover-upload {
display: flex;
align-items: flex-start;
gap: 16px;
flex-wrap: wrap;
}
.cover-preview {
width: 240px;
height: 135px; /* 16:9 */
border-radius: 10px;
background: #f0f0f2;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
border: 2px dashed #d2d2d7;
flex-shrink: 0;
}
.cover-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.cover-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #c7c7cc;
width: 100%;
height: 100%;
}
.cover-upload-actions {
display: flex;
flex-direction: column;
gap: 8px;
}
/* 宽版裁剪模态框(16:9) */
.modal-content-wide { max-width: 800px; }
.crop-container-wide {
height: auto;
aspect-ratio: 16 / 9;
max-height: 70vh;
}
/* ====== Emoji 选择器 ====== */
.emoji-picker {
display: grid;
grid-template-columns: repeat(auto-fill, 40px);
gap: 6px;
margin-top: 8px;
padding: 12px;
background: #fafafa;
border: 1px solid #e5e5e7;
border-radius: 8px;
max-height: 200px;
overflow-y: auto;
}
.emoji-btn {
width: 40px;
height: 40px;
font-size: 20px;
border: 1px solid transparent;
background: #fff;
border-radius: 6px;
cursor: pointer;
padding: 0;
line-height: 1;
transition: 0.15s;
}
.emoji-btn:hover {
background: #f0f0f2;
border-color: #d2d2d7;
}
.emoji-btn:active {
transform: scale(0.92);
}
/* ====== 出行方式类型标签 ====== */
.mode-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
background: #f0f0f2;
color: #555;
}
.mode-tag.mode-drive_own { background: #e0f0ff; color: #007aff; }
.mode-tag.mode-drive_rental { background: #fff3e0; color: #b26a00; }
.mode-tag.mode-train { background: #e8f5e9; color: #1b7a2f; }
.mode-tag.mode-plane { background: #e3f2fd; color: #0d47a1; }
.mode-tag.mode-bus { background: #fff8e1; color: #8a6d00; }
.mode-tag.mode-subway { background: #f3e5f5; color: #6a1b9a; }
.mode-tag.mode-taxi { background: #fff3cd; color: #856404; }
.mode-tag.mode-bike { background: #e0f7fa; color: #00695c; }
.mode-tag.mode-walk { background: #f1f8e9; color: #558b2f; }
.mode-tag.mode-ship { background: #e1f5fe; color: #01579b; }
.mode-tag.mode-other { background: #f0f0f2; color: #555; }
/* ====== 表单 fieldset(起点/终点分组) ====== */
.fieldset {
border: 1px solid #e5e5e7;
border-radius: 10px;
padding: 16px 20px 4px;
margin: 0 0 18px;
background: #fafafa;
}
.fieldset legend {
padding: 0 8px;
font-size: 13px;
color: #555;
font-weight: 500;
}
.fieldset .form-row:last-child { margin-bottom: 12px; }
.node-loc .loc-arrow { color: #c7c7cc; margin: 0 4px; }
.node-loc .loc-start { color: #86868b; }
.node-loc .loc-end { color: #007aff; }
/* ====== 车辆管理 ====== */
.veh-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
.veh-tag.veh-own { background: #e0f0ff; color: #007aff; }
.veh-tag.veh-rental { background: #fff3e0; color: #b26a00; }
.tag-default {
display: inline-block;
background: #34c759;
color: #fff;
font-size: 10px;
padding: 1px 5px;
border-radius: 3px;
margin-left: 6px;
vertical-align: middle;
}
.radio-group {
display: flex;
gap: 20px;
}
.radio-label {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
font-size: 14px;
margin: 0;
font-weight: normal;
}
.radio-label input { margin: 0; }
.checkbox-label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 14px;
margin: 0;
font-weight: normal;
}
.checkbox-label input { margin: 0; }
/* ====== 节点交通细节 ====== */
.node-transport {
margin-top: 6px;
font-size: 13px;
color: #1b7a2f;
background: #f0f9f2;
padding: 4px 8px;
border-radius: 6px;
display: inline-block;
}
/* ====== 高德地址自动补全(自定义下拉) ====== */
.amap-dropdown {
position: absolute;
left: 0;
right: 0;
top: 100%;
z-index: 1000;
margin-top: 4px;
background: #fff;
border: 1px solid #d2d2d7;
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
max-height: 300px;
overflow-y: auto;
}
.amap-dropdown-item {
padding: 10px 14px;
cursor: pointer;
border-bottom: 1px solid #f0f0f2;
transition: background 0.1s;
}
.amap-dropdown-item:last-child { border-bottom: none; }
.amap-dropdown-item:hover,
.amap-dropdown-item.active {
background: #f0f7ff;
}
.amap-dropdown-name {
font-size: 14px;
color: #1d1d1f;
font-weight: 500;
}
.amap-dropdown-sub {
font-size: 12px;
color: #86868b;
margin-top: 2px;
}
/* ====== 节点附件 ====== */
.node-actions {
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid #f0f0f2;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px;
}
.node-action-link {
font-size: 12px;
color: #007aff;
text-decoration: none;
}
.node-action-link:hover { text-decoration: underline; }
.node-action-form { display: inline; margin: 0; }
.node-action-btn {
font-size: 12px;
color: #007aff;
background: none;
border: 0;
padding: 0;
cursor: pointer;
font-family: inherit;
}
.node-action-btn:hover { text-decoration: underline; }
.node-action-btn.danger { color: #ff3b30; }
.node-action-note { font-size: 12px; color: #34c759; }
.status-tag {
font-size: 11px;
padding: 1px 7px;
border-radius: 4px;
white-space: nowrap;
}
.status-planned { background: #f0f0f2; color: #555; }
.status-ongoing { background: #e0f0ff; color: #007aff; }
.status-completed { background: #e3f8e7; color: #1a7f37; }
.status-cancelled { background: #ffe5e5; color: #b3261e; }
.node-cancelled .node-title { text-decoration: line-through; color: #86868b; }
.node-cancel-reason { font-size: 12px; color: #b3261e; margin-top: 4px; }
/* ====== 活动详情:封面 ====== */
.activity-cover {
width: 100%;
aspect-ratio: 16 / 9;
overflow: hidden;
border-radius: 12px;
margin-bottom: 20px;
background: #f0f0f2;
}
.activity-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.node-attachment-link {
font-size: 12px;
color: #007aff;
text-decoration: none;
}
.node-attachment-link:hover { text-decoration: underline; }
.drop-zone {
border: 2px dashed #d2d2d7;
border-radius: 12px;
padding: 32px 20px;
text-align: center;
cursor: pointer;
transition: 0.15s;
background: #fafafa;
}
.drop-zone:hover, .drop-zone.dragging {
border-color: #007aff;
background: #f0f7ff;
}
.drop-zone-inner { color: #555; }
.file-list {
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 6px;
}
.file-row {
padding: 6px 12px;
background: #f0f0f2;
border-radius: 6px;
font-size: 13px;
color: #333;
}
.attachment-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 16px;
}
.attachment-card {
background: #fff;
border: 1px solid #e5e5e7;
border-radius: 10px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.attachment-link {
display: block;
background: #f5f5f7;
aspect-ratio: 4 / 3;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
text-decoration: none;
}
.attachment-link:hover { text-decoration: none; }
.attachment-thumb {
width: 100%;
height: 100%;
object-fit: cover;
}
.attachment-icon {
font-size: 48px;
color: #86868b;
}
.attachment-meta {
padding: 8px 10px;
position: relative;
}
.attachment-name {
font-size: 13px;
color: #1d1d1f;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 20px;
}
.attachment-sub {
font-size: 11px;
color: #86868b;
margin-top: 2px;
}
.attachment-delete {
position: absolute;
top: 6px;
right: 8px;
background: transparent;
border: none;
font-size: 18px;
color: #c7c7cc;
cursor: pointer;
padding: 0 4px;
line-height: 1;
}
.attachment-delete:hover { color: #ff3b30; }
/* ====== 通用面板(节点详情页 / 评论区) ====== */
.panel {
background: #fff;
border: 1px solid #e5e5e7;
border-radius: 12px;
padding: 20px 22px;
margin-top: 20px;
}
.section-title {
font-size: 16px;
margin: 0 0 14px;
color: #1d1d1f;
}
.inline-panel {
margin-top: 14px;
border-top: 1px dashed #e5e5e7;
padding-top: 12px;
}
.inline-panel > summary {
cursor: pointer;
font-size: 14px;
color: #007aff;
list-style: none;
outline: none;
}
.inline-panel > summary::-webkit-details-marker { display: none; }
.inline-panel > summary:hover { text-decoration: underline; }
.inline-panel[open] > summary { margin-bottom: 14px; }
/* 面板内的表单不再套一层白卡片 */
.form.form-plain {
background: transparent;
border: 0;
border-radius: 0;
padding: 0;
max-width: 640px;
}
.btn-primary-sm {
background: #007aff;
color: #fff;
border-color: #007aff;
}
.btn-primary-sm:hover { background: #0066d6; }
/* ====== 节点详情:基础信息 ====== */
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 12px 20px;
background: #fff;
border: 1px solid #e5e5e7;
border-radius: 12px;
padding: 18px 22px;
}
.detail-item { display: flex; flex-direction: column; gap: 2px; }
.detail-item-wide { grid-column: 1 / -1; }
.detail-label { font-size: 12px; color: #86868b; }
.detail-value { font-size: 14px; color: #1d1d1f; }
/* ====== 活动详情:花销汇总条 ====== */
.activity-stats {
display: flex;
flex-wrap: wrap;
gap: 16px;
background: #fff;
border: 1px solid #e5e5e7;
border-radius: 10px;
padding: 10px 16px;
margin-bottom: 18px;
font-size: 13px;
color: #555;
}
.activity-stat { white-space: nowrap; }
.money-over { color: #b3261e; }
.money-under { color: #1a7f37; }
.node-action-strong { font-weight: 600; }
/* ====== 评论 ====== */
.comment-list { display: flex; flex-direction: column; gap: 14px; }
.comment-thread { border-bottom: 1px solid #f0f0f2; padding-bottom: 12px; }
.comment-thread:last-child { border-bottom: none; padding-bottom: 0; }
.comment-item { padding: 4px 0; }
.comment-head {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
flex-wrap: wrap;
}
.comment-author { font-weight: 600; color: #1d1d1f; }
.comment-time { color: #86868b; font-size: 12px; }
.comment-body {
font-size: 14px;
color: #1d1d1f;
margin: 6px 0 6px 36px;
word-break: break-word;
}
.comment-del-form { margin-left: auto; }
.comment-del {
background: none;
border: 0;
padding: 0;
font-size: 12px;
color: #c7c7cc;
cursor: pointer;
font-family: inherit;
}
.comment-del:hover { color: #ff3b30; text-decoration: underline; }
.comment-reply-box { margin-left: 36px; }
.comment-reply-box > summary {
cursor: pointer;
font-size: 12px;
color: #007aff;
list-style: none;
outline: none;
display: inline-block;
}
.comment-reply-box > summary::-webkit-details-marker { display: none; }
.comment-reply-box > summary:hover { text-decoration: underline; }
.comment-replies {
margin-left: 36px;
margin-top: 10px;
padding-left: 14px;
border-left: 2px solid #f0f0f2;
display: flex;
flex-direction: column;
gap: 10px;
}
.comment-form { margin-top: 16px; }
.comment-form-inline { margin-top: 8px; }
.comment-form textarea {
width: 100%;
padding: 10px 12px;
border: 1px solid #d2d2d7;
border-radius: 8px;
font-size: 14px;
font-family: inherit;
resize: vertical;
background: #fff;
}
.comment-form textarea:focus {
outline: none;
border-color: #007aff;
box-shadow: 0 0 0 3px rgba(0,122,255,0.15);
}
.comment-form .form-actions { padding-top: 8px; }
/* ====== 参与人 ====== */
.member-list { display: flex; flex-direction: column; gap: 8px; }
.member-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
padding: 8px 12px;
background: #fafafa;
border: 1px solid #f0f0f2;
border-radius: 8px;
}
.member-name { font-size: 14px; font-weight: 500; color: #1d1d1f; }
.member-role {
font-size: 12px;
padding: 1px 8px;
border-radius: 4px;
background: #e0f0ff;
color: #0066d6;
}
.member-emotion { font-size: 13px; color: #b26a00; }
.member-checkin { font-size: 12px; color: #1a7f37; }
.member-del-form { margin-left: auto; }
/* 情绪单选:只显示表情,文字用 title 提示 */
.emotion-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
gap: 6px;
padding: 10px;
background: #fafafa;
border: 1px solid #e5e5e7;
border-radius: 8px;
}
.emotion-option {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
padding: 6px 4px;
border: 1px solid transparent;
border-radius: 8px;
cursor: pointer;
margin: 0;
font-weight: normal;
}
.emotion-option:hover { background: #fff; border-color: #d2d2d7; }
.emotion-option input { margin: 0; }
.emotion-option:has(input:checked) { background: #fff; border-color: #007aff; }
.emotion-face { font-size: 20px; line-height: 1.2; }
.emotion-text { font-size: 11px; color: #86868b; }
.check-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 6px 12px;
}
/* ====== 花销 ====== */
.expense-summary {
display: flex;
flex-wrap: wrap;
gap: 24px;
padding: 12px 16px;
background: #fafafa;
border: 1px solid #f0f0f2;
border-radius: 10px;
margin-bottom: 14px;
}
.expense-stat { display: flex; flex-direction: column; gap: 2px; }
.expense-stat .muted { font-size: 12px; }
.expense-stat strong { font-size: 16px; }
.expense-list { display: flex; flex-direction: column; gap: 8px; }
.expense-row {
border: 1px solid #f0f0f2;
border-radius: 8px;
padding: 8px 12px;
background: #fff;
}
.expense-main {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.expense-cat {
font-size: 12px;
padding: 1px 8px;
border-radius: 4px;
background: #f0f0f2;
color: #555;
}
.expense-kind { font-size: 11px; padding: 1px 6px; border-radius: 4px; }
.expense-kind-budget { background: #fff3e0; color: #b26a00; }
.expense-kind-actual { background: #e3f8e7; color: #1a7f37; }
.expense-desc { font-size: 14px; color: #1d1d1f; }
.expense-amount {
margin-left: auto;
font-size: 15px;
font-weight: 600;
color: #1d1d1f;
}
.expense-sub {
font-size: 12px;
color: #86868b;
margin-top: 4px;
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}