v24 第 12 批:5 条(删除按钮位置/日期控件三修/日历跳转/节气节日/财务图)
用户 2026-10-05 的 5 条。第 11 批交付后用户追问"确定这些需求都做完了?"—— 没有, 这 5 条是进一步要求。实现过程中查出 4 个真 bug。 #1 详情页删除按钮紧贴「开通账号」 上一版放在页面最下方独立区块(danger-zone),用户要「就放在开通账号按钮下面, 紧挨着,长度大小和开通账号一样,颜色改成红色」。→ 搬进「登录账号」卡片内 (.del-inline + 细分隔线 + .btn-block-lg)。 线上几何实测:同一张卡片、左边缘对齐 255==255、宽度一致 990==990、红色 rgb(192,83,63)。 #2 日期控件三修(两个真 bug + 一次闪烁) · 灰底:Semi **外层** .semi-datepicker-input 自带 rgba(46,50,56,.05)、高 32px; 内层 wrapper 白底 40px → 灰的露在外面。实测「灰底元素」= 1 个 → 修后 0 个。 · 不垂直居中:外层 32px < 内层 40px,实测 gapTop=1 / **gapBottom=17**(文字贴顶) → 修后 gapTop=1 / gapBottom=1。 · 闪烁:实测首 **200ms** 原生输入框可见(要等 3.4MB React 解析)。 用 no-js/js 二态:<html class="no-js"> + head 内联脚本立刻换 js + CSS `html.js .dt-input:not(.is-semi) .dt-native { visibility:hidden }` (配同高骨架 ::after 防布局跳动)。实测「出现过原生可见」= False。 #3 日历跳转保留视图 + 下拉日历 原来写死 view=day(在月视图点跳转会被踢进日视图)。→ 表单带当前 $view(月还带 ym); 选择器从"居中弹窗"改成**按钮下方下拉** .cal-jump-drop。 实测:月视图跳 8/15 仍在月视图且是 8 月;周视图跳 8/15 → 落 8/10 那一周; 日视图跳 8/15 → 显示 8/15。 #4 传统节日 + 二十四节气两个系统本(Schema v18) Holiday::SOLAR_TERMS(24 项 C 常数近似公式 + 例外表)+ traditionalRange (农历推导;除夕用"次日是否正月初一"反推,写死腊月廿九会有一半年份错)。 实测 2026:节气 24 条全对(清明 4/5、冬至 12/22);春节 2/17、除夕 2/16、中秋 9/25。 ⚠️ ENUM 必须显式 ALTER(对已存在的表不生效)——同一坑第二次踩,VERSION 17→18。 #5 财务(一个真 bug + 一个假起点 + 一个真 bug) · chart_line 读 `cents` 而 timeline() 返回 `net` → 键名不匹配,**总资产图每点读成 0** (正是用户说的"图上是 0")。修:兼容 cents ?? net ?? asset。 实测 timeline net=800000(8000 元),线上 polyline 有真实起伏。 · 去掉 timeline 人为插的"前一天=0"假起点(用户:"总资产不应该是 0, 应该是所有账目创建时的金额的总数")。 · ppSetSelected 只用 document.querySelector 按名字找,而财务页有**两个** person_id 组件(「记一笔」+ 修改弹框)→ 永远灌到第一个。加 root 参数、调用方传 panel。 验证:e2e 786/0、交互 92/0、升级 129/0(+3 条 v18 断言)、batch_e 91/0、batch10 46/0、 batch9 38/0、picker_unify 28/0、picker_lazy 11/0、crop 17/0、formbloat 9/0、semi 27/0、 mobile 688/0、drag 5/0、geom 5/5;第 12 批专项:财务 7/0、日历 19/0。 部署静态断言新增 5 组(含反向验证:注入回退 → FAIL=1)。 线上自检 19/19;线上真实账号验收 **30/0**(含真实浏览器几何量删除按钮位置)。
This commit is contained in:
@@ -77,25 +77,34 @@ $headText = $view === 'month' ? date('Y 年 n 月', $ts)
|
||||
⚠️ 用原生 <input type="date"> 而不是再引一个弹层:它的原生日历足够用,
|
||||
而且**选中就有值**(change 事件)→ 直接跳转,不用再点一次"确定"。
|
||||
桌面/手机/无 JS 三种情况下的行为都是可预期的。 */ ?>
|
||||
<?php /* 用户 2026-10-04 第 11 批 #6 原话(两句):
|
||||
· 「跳转日期那个日期选择框,用我之前给你的那个,保持所有样式和格式的统一。」
|
||||
· 「跳转日期那个地方,不要那么长,只需要和设置一样,一个按钮即可。」
|
||||
→ 从"一个长条(图标 + 文字 + 原生日期框)"改成**与设置按钮同规格的单个图标按钮**
|
||||
(btn-icon,与 ☰ 同宽同高),点开弹出 Semi 日期选择器(与全站日期控件同一套样式),
|
||||
选中即跳到那天的日视图。
|
||||
原生 input[type=date] 仍然保留(noscript 里)——"没 JS 也能跳日期"这件事没丢。 */ ?>
|
||||
<?php /* 用户 2026-10-05 第 12 批 #3 原话(三句):
|
||||
· 「跳转到某一天时,不要进入日视图,保留当前本来的视图,只不过是跳转到这一天,
|
||||
如果是周视图,那么就是跳转到这一天所在的周,如果是月视图,则跳转到这一天所在的月」
|
||||
· 「选择这个日期,不用弹窗,直接把日历选择,弹出在这个按钮下面就可以了」
|
||||
→ ① 不再固定 view=day:带上**当前视图**(月/周/日各自跳到对应跨度);
|
||||
② 日期选择从"居中弹窗"改成**贴着按钮下方展开的下拉日历**。 */ ?>
|
||||
<form class="cal-jump" method="get" action="index.php">
|
||||
<input type="hidden" name="r" value="calendar">
|
||||
<input type="hidden" name="view" value="day">
|
||||
<?php /* 保留当前视图:月→该月 / 周→那一周 / 日→那一天。
|
||||
⚠️ 原来写死 view=day,用户点"跳到 10 月 20 日"就被踢进日视图,
|
||||
而他本来在月视图里 —— 这正是不满的点。 */ ?>
|
||||
<input type="hidden" name="view" value="<?= e($view) ?>">
|
||||
<input type="hidden" name="d" id="calJumpD" value="<?= e($anchor) ?>">
|
||||
<?php /* 月视图用的是 ym(年月),跳转时也要带上,否则服务端会退回默认月 */ ?>
|
||||
<?php if ($view === 'month'): ?>
|
||||
<input type="hidden" name="ym" id="calJumpYM" value="<?= e(substr($anchor, 0, 7)) ?>">
|
||||
<?php endif; ?>
|
||||
<button type="button" class="btn btn-icon cal-jump-btn" id="calJumpBtn"
|
||||
title="跳到某一天" aria-label="跳到某一天">
|
||||
title="跳到某一天" aria-label="跳到某一天" aria-haspopup="dialog" aria-expanded="false">
|
||||
<svg viewBox="0 0 16 16" width="18" height="18" aria-hidden="true" fill="none"
|
||||
stroke="currentColor" stroke-width="1.7" stroke-linecap="round">
|
||||
<rect x="2" y="3.2" width="12" height="10.8" rx="1.6"/>
|
||||
<path d="M2 6.6h12M5.2 1.8v2.6M10.8 1.8v2.6"/>
|
||||
</svg>
|
||||
</button>
|
||||
<?php /* 下拉日历的宿主:紧贴按钮下方展开(不是弹窗)。JS 就绪后把 Semi
|
||||
日期选择器挂进来;JS 不可用时它是空的、退到下面的 noscript。 */ ?>
|
||||
<div class="cal-jump-drop" id="calJumpDrop" hidden></div>
|
||||
<noscript>
|
||||
<input class="input" type="date" name="d" value="<?= e($anchor) ?>" aria-label="选择日期">
|
||||
<button class="btn btn-sm" type="submit">跳转</button>
|
||||
@@ -343,69 +352,85 @@ $headText = $view === 'month' ? date('Y 年 n 月', $ts)
|
||||
/* 「全天」勾上藏掉时刻那一段的逻辑在 _event_fields.php 里(两个页面共用一份) */
|
||||
})();
|
||||
|
||||
/* ---- 用户第 6 条:「跳到某一天」—— 单个图标按钮 + Semi 日期选择器 ---- */
|
||||
/* ---- v24 第 12 批 #3:「跳到某一天」—— 单个图标按钮 + **按钮下方的下拉日历** ----
|
||||
用户原话:「选择这个日期,不用弹窗,直接把日历选择,弹出在这个按钮下面就可以了。」
|
||||
所以从"居中弹窗(cal-jump-host 全屏遮罩)"改成"贴着按钮下方展开的下拉"。
|
||||
⚠️ 中途会经过"保留视图"那一关:jump() 提交时带上当前 view(月的 ym / 周的 d / 日的 d),
|
||||
由服务端决定该视图下这个日期落在哪一屏 —— 见 index.php 的 calendar 路由。 */
|
||||
(function () {
|
||||
var form = document.querySelector('.cal-jump');
|
||||
var btn = document.getElementById('calJumpBtn');
|
||||
var hid = document.getElementById('calJumpD');
|
||||
var drop = document.getElementById('calJumpDrop');
|
||||
if (!form || !btn || !hid) { return; }
|
||||
var root = null; // React root(重开时先 unmount)
|
||||
|
||||
function jump(v) {
|
||||
if (!v) { return; }
|
||||
hid.value = v;
|
||||
// 月视图还要带上 ym(服务端按月取数),否则会跳回默认月
|
||||
var ym = document.getElementById('calJumpYM');
|
||||
if (ym) { ym.value = String(v).slice(0, 7); }
|
||||
form.submit();
|
||||
}
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
var meta = document.querySelector('meta[name="fb-semi"]');
|
||||
var conf = null;
|
||||
try { conf = meta ? JSON.parse(meta.getAttribute('content')) : null; } catch (e) { conf = null; }
|
||||
function closeDrop() {
|
||||
if (!drop) { return; }
|
||||
drop.hidden = true;
|
||||
btn.setAttribute('aria-expanded', 'false');
|
||||
if (root) { root.unmount(); root = null; }
|
||||
drop.innerHTML = '';
|
||||
}
|
||||
|
||||
function fallback() {
|
||||
// Semi 没就绪 → 用一个临时原生日期框(无 JS 时的兜底与这里同一行为)
|
||||
var t = document.createElement('input');
|
||||
t.type = 'date';
|
||||
t.value = hid.value;
|
||||
t.style.position = 'fixed';
|
||||
t.style.left = '-9999px';
|
||||
document.body.appendChild(t);
|
||||
t.addEventListener('change', function () { jump(t.value); });
|
||||
if (typeof t.showPicker === 'function') { try { t.showPicker(); } catch (e2) {} }
|
||||
else { t.focus(); t.click(); }
|
||||
}
|
||||
// 原生兜底:Semi 没就绪时用临时原生日期框(与无 JS 时行为一致)
|
||||
function fallback() {
|
||||
var t = document.createElement('input');
|
||||
t.type = 'date';
|
||||
t.value = hid.value;
|
||||
t.style.position = 'fixed';
|
||||
t.style.left = '-9999px';
|
||||
document.body.appendChild(t);
|
||||
t.addEventListener('change', function () { jump(t.value); });
|
||||
if (typeof t.showPicker === 'function') { try { t.showPicker(); } catch (e2) {} }
|
||||
else { t.focus(); t.click(); }
|
||||
}
|
||||
|
||||
btn.addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
if (!drop) { fallback(); return; }
|
||||
if (!drop.hidden) { closeDrop(); return; }
|
||||
|
||||
if (!window.SemiUI || !window.SemiUI.DatePicker || !window.ReactDOM) { fallback(); return; }
|
||||
|
||||
// 弹一个**只有一个日期选择器**的轻浮层(与全站日期控件同一套样式)
|
||||
var host = document.getElementById('calJumpHost');
|
||||
if (!host) {
|
||||
host = document.createElement('div');
|
||||
host.id = 'calJumpHost';
|
||||
host.className = 'cal-jump-host';
|
||||
document.body.appendChild(host);
|
||||
}
|
||||
var wrap = document.createElement('div');
|
||||
wrap.className = 'cal-jump-pop';
|
||||
wrap.innerHTML = '<div class="cal-jump-pop-head">跳到哪一天?' +
|
||||
'<button type="button" class="cal-jump-x" aria-label="关闭">✕</button></div>';
|
||||
drop.hidden = false;
|
||||
btn.setAttribute('aria-expanded', 'true');
|
||||
var inner = document.createElement('div');
|
||||
wrap.appendChild(inner);
|
||||
host.innerHTML = '';
|
||||
host.appendChild(wrap);
|
||||
|
||||
ReactDOM.createRoot(inner).render(React.createElement(SemiUI.DatePicker, {
|
||||
drop.innerHTML = '';
|
||||
drop.appendChild(inner);
|
||||
root = ReactDOM.createRoot(inner);
|
||||
root.render(React.createElement(SemiUI.DatePicker, {
|
||||
type: 'date',
|
||||
format: 'yyyy-MM-dd',
|
||||
defaultValue: hid.value ? new Date(hid.value + 'T00:00:00') : new Date(),
|
||||
open: true,
|
||||
// 选中即跳,不用再点"确定"
|
||||
// ⚠️ 不用 `open: true`:那会让 Semi 自己弹它的浮层(就又成弹窗了)。
|
||||
// 这里要的是"日历长在下拉容器里",所以让它以内联面板渲染。
|
||||
open: false,
|
||||
defaultOpen: true,
|
||||
preventScroll: true,
|
||||
onChange: function (d, ds) {
|
||||
if (ds) { jump(ds); }
|
||||
},
|
||||
onOpenChange: function (o) { if (!o) { host.innerHTML = ''; } }
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
wrap.querySelector('.cal-jump-x').addEventListener('click', function () { host.innerHTML = ''; });
|
||||
// 点外面 / Esc 收起(下拉的常规行为)
|
||||
document.addEventListener('click', function (e) {
|
||||
if (!drop || drop.hidden) { return; }
|
||||
if (drop.contains(e.target) || btn.contains(e.target)) { return; }
|
||||
closeDrop();
|
||||
});
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape') { closeDrop(); }
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user