fix: 返回键失效的根因 + 同步页重做 + 连接池会永久阻塞线程
返回键
- 根因是 Framework7 的页面过渡由动画事件驱动:push 时把 allowPageChange
置 false,等动画报告结束再恢复。这个报告不来,路由就永久卡住,之后每次
导航都被静默丢弃,back() 还会把上一页重建一份而不是弹出。
实测对照:navigate({animate:false}) 前后状态完全正确,带动画则必卡。
因此关掉页面过渡动画——导航同步完成,处处正确。动效改由内容承担
(卡片入场、hero 揭示、顶部进度条),这个取舍里正确性优先。
- Screen 的返回改为显式 handler,先清掉残留过渡状态再 back(),
不依赖路由自己的闸门。注意只清视图上的 router-transition 类:
页面自身的 page-previous 是 F7 判断「回到哪一页」的依据,
一并清掉会导致重建出一个重复的页面(中途踩过这个坑)。
同步页
- .btn 系列样式原本只定义在 pages/Pages.css,而那个文件只被一个没有路由的
遗留页面引用,所以真实页面上按钮全都退化成 Framework7 的默认样式——
就是你看到的三条链接。样式移进每个界面都会加载的 Screen.css。
- 主次分明:一个填充主按钮 + 两个带副标题的次按钮;补上「同步会取哪些数据」
说明,页面不再是一大片空白。
- 进度条显示当前阶段(每日数据 2026-08-01 / 运动详情 12/174 / 身体成分…),
原来只有「0 / 730 天」,几分钟里完全看不出在做什么。
后端
- MariaDB 连接池:_mariadb_release 用的是阻塞 put(),而队列 maxsize=10,
_mariadb_acquire 在池空时又会新建连接。并发超过 10 之后,归还的线程会
永久停在 put() 上,请求就此挂死。改为 put_nowait,多出来的连接直接关闭。
- 进程重启会带走同步线程却留下 status=syncing 的行,界面上是一个永远不动
的进度条,还拒绝开始新同步。启动时清理。
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,47 @@
|
||||
/* Buttons -----------------------------------------------------------------
|
||||
These lived in pages/Pages.css, which only one unrouted leftover page
|
||||
imported — so on every real screen the .btn classes resolved to nothing and
|
||||
Framework7's default button styling showed through instead. They belong
|
||||
here, in the stylesheet every screen loads. */
|
||||
.btn {
|
||||
padding: 0.7rem 1.1rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 11px;
|
||||
font-size: 0.92rem;
|
||||
font-weight: 550;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
width: auto;
|
||||
transition: transform 0.15s var(--ease), filter 0.15s var(--ease),
|
||||
border-color 0.15s var(--ease), background 0.15s var(--ease);
|
||||
}
|
||||
|
||||
.btn:active:not(:disabled) { transform: scale(0.975); }
|
||||
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
|
||||
.btn-primary { background: var(--accent-solid); color: #fff; }
|
||||
.btn-primary:hover:not(:disabled) { filter: brightness(1.08); }
|
||||
|
||||
.btn-plain {
|
||||
background: var(--surface-1);
|
||||
border-color: var(--border);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-plain:hover:not(:disabled) { border-color: var(--border-strong); }
|
||||
|
||||
.btn-large { width: 100%; padding: 0.85rem 1.5rem; font-size: 1rem; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.btn { transition: none; }
|
||||
.btn:active:not(:disabled) { transform: none; }
|
||||
}
|
||||
|
||||
/* Sections ---------------------------------------------------------------- */
|
||||
.sec {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ReactNode, useEffect, useState } from 'react';
|
||||
import { Page, Navbar, NavRight, f7 } from 'framework7-react';
|
||||
import { Page, Navbar, NavLeft, NavRight, Link, f7 } from 'framework7-react';
|
||||
import { apiClient } from '../services/api';
|
||||
import './Screen.css';
|
||||
|
||||
@@ -16,6 +16,34 @@ interface ScreenProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go back without depending on the router's own guard.
|
||||
*
|
||||
* Framework7 sets `router.allowPageChange = false` for the duration of a page
|
||||
* transition and restores it when the transition reports it has ended. When
|
||||
* that report never arrives the flag stays false and `router.back()` becomes a
|
||||
* no-op — a back button that does nothing, with a reload as the only way out.
|
||||
* Clearing the leftover transition state first makes the button work whether
|
||||
* or not the router got stuck.
|
||||
*/
|
||||
function goBack() {
|
||||
const router = f7.views.current?.router;
|
||||
if (!router) return;
|
||||
|
||||
// Only the view's transition flags are cleared. The pages' own
|
||||
// page-previous / page-next classes are how F7 knows which page to return
|
||||
// to — stripping those makes `back()` rebuild the previous screen from
|
||||
// scratch and stack a duplicate instead of popping.
|
||||
const el = router.view?.el as HTMLElement | undefined;
|
||||
el?.classList.remove(
|
||||
'router-transition', 'router-transition-forward',
|
||||
'router-transition-backward', 'router-transition-css-forward',
|
||||
'router-transition-css-backward'
|
||||
);
|
||||
router.allowPageChange = true;
|
||||
router.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Common page chrome.
|
||||
*
|
||||
@@ -44,7 +72,18 @@ function Screen({
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Navbar large={large} transparent={large} title={title} subtitle={subtitle} backLink={backLink ? '返回' : undefined}>
|
||||
<Navbar large={large} transparent={large} title={title} subtitle={subtitle}>
|
||||
{backLink && (
|
||||
<NavLeft>
|
||||
<Link
|
||||
className="back-link"
|
||||
onClick={goBack}
|
||||
iconIos="f7:chevron_left"
|
||||
iconMd="f7:chevron_left"
|
||||
aria-label="返回"
|
||||
/>
|
||||
</NavLeft>
|
||||
)}
|
||||
{/* Rendered only when the screen actually has an action: an empty
|
||||
NavRight still paints a container, which showed up as a stray
|
||||
grey box beside the title. */}
|
||||
|
||||
Reference in New Issue
Block a user