下拉菜单改成极简小浮窗(参考家书 FamilyBook 的 popup 做法)
用户第三次指出这个按钮不对,原话:
「你不用做那么复杂,就是参考一下 FamilyBook 里边那个设置按钮,
非常简单的,点了之后就拉下来一个很窄的一个框,
把所有的内容都铺在上面就可以了」
去看了 FamilyBook 的实现:顶栏那颗按钮是 bindSheet + SheetSize.FIT_CONTENT,
即「内容多高就多高」的小浮层,不是整幅面板。
改法:
- 面板从「left:0 right:0 铺满宽度 + 遮罩 + 锁滚动」
改成「贴按钮右下、min-width 172px、内容撑高、圆角 12px 小浮窗」
- 删掉 sw-mask 遮罩、删掉 body overflow 锁定
- 面板移进 .topbar .wrap(flex 内),以它为定位参照,右对齐按钮
- 删掉菜单里「选择软件」标题(按钮本身已经说明用途)
- 条目字号降到 14px、图标 18px、内边距收紧,贴合窄框
- 「当前」标记从胶囊缩成 6px 小圆点,窄框里不占地方
- 按钮简化:去掉边框与三横线变×的动效,只留 hover 浅底
实测(CDP 真实浏览器):下拉框 172×138px,仅占视口宽 13.7%,
右边缘与按钮相差 4px;390px 与 1280px 两种宽度下各 15 项断言全过
(浮窗非整幅、无遮罩、未锁滚动、贴按钮下方、右对齐、
高度由内容决定、有圆角边框、条目纵排同x、y递增、点外部可关)。
This commit is contained in:
+10
-31
@@ -801,46 +801,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- 顶栏软件抽屉(右上角汉堡按钮) ----------
|
/* ---------- 顶栏软件下拉菜单(右上角三横线) ----------
|
||||||
* 原来「选择软件」平铺在列表上方占一整行,软件一多就把内容往下推。
|
* 极简:点了就展开一个小浮窗,点外面 / Esc 就关。
|
||||||
* 现在收进抽屉:点击展开 / 再次点击收起 / 点外部关闭 / Esc 关闭。
|
* 不做遮罩、不锁背景滚动 —— 那套是侧边栏的做法,下拉菜单不需要。 */
|
||||||
* 用 hidden 属性而非纯 class,保证无 JS 时抽屉默认不显示(不会闪一下)。 */
|
|
||||||
function bindSoftwarePanel() {
|
function bindSoftwarePanel() {
|
||||||
var btn = document.getElementById('swToggle');
|
var btn = document.getElementById('swToggle');
|
||||||
var panel = document.getElementById('swPanel');
|
var panel = document.getElementById('swPanel');
|
||||||
var mask = document.getElementById('swMask');
|
|
||||||
if (!btn || !panel) { return; }
|
if (!btn || !panel) { return; }
|
||||||
|
|
||||||
function open() {
|
|
||||||
panel.hidden = false;
|
|
||||||
if (mask) { mask.hidden = false; }
|
|
||||||
btn.setAttribute('aria-expanded', 'true');
|
|
||||||
document.body.style.overflow = 'hidden'; // 抽屉开着时锁住背景滚动
|
|
||||||
}
|
|
||||||
function close() {
|
function close() {
|
||||||
panel.hidden = true;
|
panel.hidden = true;
|
||||||
if (mask) { mask.hidden = true; }
|
|
||||||
btn.setAttribute('aria-expanded', 'false');
|
btn.setAttribute('aria-expanded', 'false');
|
||||||
document.body.style.overflow = '';
|
|
||||||
}
|
|
||||||
function toggle() {
|
|
||||||
if (panel.hidden) { open(); } else { close(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.addEventListener('click', function (e) {
|
btn.addEventListener('click', function (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
toggle();
|
if (panel.hidden) {
|
||||||
|
panel.hidden = false;
|
||||||
|
btn.setAttribute('aria-expanded', 'true');
|
||||||
|
} else {
|
||||||
|
close();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 点遮罩关闭(遮罩就是「下面的内容」,点了就是点外部)
|
// 点菜单内部不算点外面
|
||||||
if (mask) {
|
|
||||||
mask.addEventListener('click', function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点面板内部不算点外部
|
|
||||||
panel.addEventListener('click', function (e) { e.stopPropagation(); });
|
panel.addEventListener('click', function (e) { e.stopPropagation(); });
|
||||||
|
|
||||||
document.addEventListener('click', function () {
|
document.addEventListener('click', function () {
|
||||||
@@ -850,11 +834,6 @@
|
|||||||
document.addEventListener('keydown', function (e) {
|
document.addEventListener('keydown', function (e) {
|
||||||
if (e.key === 'Escape' && !panel.hidden) { close(); btn.focus(); }
|
if (e.key === 'Escape' && !panel.hidden) { close(); btn.focus(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
// 转到别的页面时把滚动锁解掉,避免返回后页面不能滚
|
|
||||||
window.addEventListener('pagehide', function () {
|
|
||||||
document.body.style.overflow = '';
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- 邮件测试发送(后台设置页) ---------- */
|
/* ---------- 邮件测试发送(后台设置页) ---------- */
|
||||||
|
|||||||
+45
-44
@@ -33,7 +33,7 @@ p{margin:8px 0}
|
|||||||
border-bottom:1px solid var(--line);
|
border-bottom:1px solid var(--line);
|
||||||
/* 软件抽屉是 absolute + top:100%,需要它来做定位参照,否则会跑到页面最上面 */
|
/* 软件抽屉是 absolute + top:100%,需要它来做定位参照,否则会跑到页面最上面 */
|
||||||
}
|
}
|
||||||
.topbar .wrap{display:flex; align-items:center; gap:14px; height:66px}
|
.topbar .wrap{display:flex; align-items:center; gap:14px; height:66px; position:relative}
|
||||||
.topbar .brand-mini{display:flex; align-items:center; gap:10px; font-weight:700; font-size:17px; color:var(--fg); min-width:0}
|
.topbar .brand-mini{display:flex; align-items:center; gap:10px; font-weight:700; font-size:17px; color:var(--fg); min-width:0}
|
||||||
.topbar .brand-mini:hover{text-decoration:none; color:var(--fg)}
|
.topbar .brand-mini:hover{text-decoration:none; color:var(--fg)}
|
||||||
/* 顶栏里的软件标识:17px 字 + 32px 图标 ——
|
/* 顶栏里的软件标识:17px 字 + 32px 图标 ——
|
||||||
@@ -47,64 +47,65 @@ p{margin:8px 0}
|
|||||||
.topbar .brand-mini .sw-badge{gap:7px}
|
.topbar .brand-mini .sw-badge{gap:7px}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- 右上角汉堡按钮 + 软件抽屉 ---------- */
|
/* ---------- 右上角三横线按钮 + 下拉菜单 ---------- */
|
||||||
.sw-toggle{
|
.sw-toggle{
|
||||||
flex:none; width:40px; height:40px; padding:0; margin-left:2px;
|
flex:none; width:38px; height:38px; padding:0; margin-left:2px;
|
||||||
display:inline-flex; flex-direction:column; align-items:center; justify-content:center; gap:5px;
|
display:inline-flex; flex-direction:column; align-items:center; justify-content:center; gap:4px;
|
||||||
background:transparent; border:1px solid var(--line); border-radius:10px;
|
background:transparent; border:0; border-radius:9px;
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
}
|
}
|
||||||
.sw-toggle span{
|
.sw-toggle span{
|
||||||
display:block; width:17px; height:2px; border-radius:2px;
|
display:block; width:18px; height:2px; border-radius:2px;
|
||||||
background:var(--fg); transition:transform .18s ease, opacity .18s ease;
|
background:var(--fg);
|
||||||
}
|
}
|
||||||
.sw-toggle:hover{border-color:var(--brand); background:var(--brand-soft)}
|
.sw-toggle:hover{background:var(--brand-soft)}
|
||||||
.sw-toggle[aria-expanded="true"]{border-color:var(--brand); background:var(--brand-soft)}
|
.sw-toggle[aria-expanded="true"]{background:var(--brand-soft)}
|
||||||
/* 展开时变成 × */
|
|
||||||
.sw-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
|
|
||||||
.sw-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
|
|
||||||
.sw-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
|
|
||||||
|
|
||||||
/* ---------- 软件抽屉:一律竖排浮层,覆盖在正文之上 ----------
|
/* ---------- 软件下拉菜单(右上角三横线) ----------
|
||||||
* 用户 2026-11-03:「都保持统一,点了之后下拉出来一个条一个条竖着排列,
|
* 参考家书 FamilyBook 顶栏那个 popup 的做法:**内容多高就多高的小浮窗**,
|
||||||
* 把下面的内容全部盖住」。
|
* 紧贴按钮下方、宽度由内容撑开,不是铺满整屏的面板,也不加遮罩。
|
||||||
* 早期实现是宽屏横排、窄屏竖排两套,结果手机上会跟正文撞在一起;
|
* 用户 2026-11-03:「不用做那么复杂,就是点了之后拉下来一个很窄的框,
|
||||||
* 现在只有一种形态:绝对定位的浮卡,overlay 盖住页面内容。 */
|
* 把所有的内容都铺在上面就可以了」。
|
||||||
|
*
|
||||||
|
* ⚠️ 用过两版都不对:
|
||||||
|
* ① 宽屏横排 / 窄屏竖排两套 —— 手机上和正文撞在一起
|
||||||
|
* ② 整幅 absolute 面板 + 遮罩 —— 太重了,像侧边栏而不是下拉菜单
|
||||||
|
* 现在只有这一种形态:小浮窗,贴按钮右侧对齐。 */
|
||||||
.sw-panel{
|
.sw-panel{
|
||||||
position:absolute; left:0; right:0; top:100%;
|
position:absolute; top:calc(100% - 4px); right:12px;
|
||||||
z-index:60;
|
z-index:60;
|
||||||
|
min-width:172px; max-width:min(280px, calc(100vw - 24px));
|
||||||
|
padding:6px;
|
||||||
background:var(--card);
|
background:var(--card);
|
||||||
border-top:1px solid var(--line);
|
border:1px solid var(--line);
|
||||||
border-bottom:1px solid var(--line);
|
border-radius:12px;
|
||||||
box-shadow:0 18px 40px -12px rgba(20,30,50,.28);
|
box-shadow:0 10px 28px -8px rgba(20,30,50,.28), 0 2px 6px rgba(20,30,50,.08);
|
||||||
max-height:calc(100vh - 66px);
|
max-height:calc(100vh - 90px);
|
||||||
overflow:auto;
|
overflow:auto;
|
||||||
}
|
}
|
||||||
.sw-panel .wrap{padding-top:14px; padding-bottom:14px}
|
/* 菜单里不再需要「选择软件」这个标题 —— 按钮本身已经说明用途 */
|
||||||
.sw-panel-hd{
|
.sw-panel-hd{display:none}
|
||||||
font-size:13px; font-weight:600; color:var(--muted);
|
.sw-panel .wrap{padding:0; max-width:none}
|
||||||
margin-bottom:8px; letter-spacing:.2px;
|
/* 一条一行,竖排 */
|
||||||
}
|
.sw-panel-list{display:flex; flex-direction:column; gap:1px}
|
||||||
/* 永远竖排:一条一行,不再有宽屏横排分支 */
|
|
||||||
.sw-panel-list{display:flex; flex-direction:column; gap:2px}
|
|
||||||
.sw-item{
|
.sw-item{
|
||||||
display:flex; align-items:center; gap:10px;
|
display:flex; align-items:center; gap:8px;
|
||||||
padding:10px 12px; border-radius:10px; color:var(--fg);
|
padding:8px 10px; border-radius:8px; color:var(--fg);
|
||||||
border:1px solid transparent;
|
font-size:14px; white-space:nowrap;
|
||||||
|
border:0;
|
||||||
}
|
}
|
||||||
.sw-item:hover{background:var(--bg, #f6f8fb); text-decoration:none; color:var(--fg)}
|
.sw-item:hover{background:var(--brand-soft); text-decoration:none; color:var(--fg)}
|
||||||
.sw-item.on{background:var(--brand-soft); border-color:var(--brand-soft)}
|
.sw-item.on{background:var(--brand-soft)}
|
||||||
.sw-item.on .sw-badge-nm{font-weight:700}
|
.sw-item.on .sw-badge-nm{font-weight:700}
|
||||||
|
.sw-item .sw-badge{gap:7px}
|
||||||
|
.sw-item .sw-badge-nm{font-size:14px; font-weight:500}
|
||||||
|
.sw-item .sw-badge-img{width:18px !important; height:18px !important}
|
||||||
|
/* 「当前」标记缩成一个小圆点,别在窄框里占地方 */
|
||||||
.sw-item-cur{
|
.sw-item-cur{
|
||||||
margin-left:auto; font-size:11.5px; font-weight:600; color:var(--brand);
|
margin-left:auto;
|
||||||
background:var(--card); border:1px solid var(--brand);
|
width:6px; height:6px; flex:none; border-radius:50%;
|
||||||
padding:1px 8px; border-radius:99px;
|
background:var(--brand);
|
||||||
}
|
font-size:0; line-height:0; overflow:hidden; text-indent:-999px;
|
||||||
|
|
||||||
/* 抽屉打开时给正文加一层遮罩,点它也能关 —— 视觉上更明确「盖住了」 */
|
|
||||||
.sw-mask{
|
|
||||||
position:fixed; inset:0; z-index:55;
|
|
||||||
background:rgba(15,23,35,.18);
|
|
||||||
}
|
}
|
||||||
.topbar .brand-text{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
|
.topbar .brand-text{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
|
||||||
.topbar .logo-mark{
|
.topbar .logo-mark{
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ if (!defined('FB_ROOT')) {
|
|||||||
define('FB_ROOT', dirname(__DIR__));
|
define('FB_ROOT', dirname(__DIR__));
|
||||||
// 静态资源版本号:改 assets/ 下的 css/js 后把这个数字 +1,
|
// 静态资源版本号:改 assets/ 下的 css/js 后把这个数字 +1,
|
||||||
// 让浏览器与 CDN 缓存失效。全站只此一处,别再往页面里写死数字。
|
// 让浏览器与 CDN 缓存失效。全站只此一处,别再往页面里写死数字。
|
||||||
if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 21); }
|
if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 28); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
|
|||||||
+8
-16
@@ -88,24 +88,16 @@ function fb_layout_head($title, array $opt = []) {
|
|||||||
<a class="btn-mini hide-sm" href="new.php">我要反馈</a>
|
<a class="btn-mini hide-sm" href="new.php">我要反馈</a>
|
||||||
|
|
||||||
<?php if (count($swList) > 1): ?>
|
<?php if (count($swList) > 1): ?>
|
||||||
<?php /* 软件切换收进右上角汉堡菜单:
|
<?php /* 右上角三横线:点开就是一个小下拉菜单。
|
||||||
* 原来平铺一行「选择软件:」+ 若干 chip,占掉一整行,
|
不用整页平铺「选择软件:」,也不必做成侧边栏。 */ ?>
|
||||||
* 而且软件一多就把列表往下推。收进抽屉后页面更干净,
|
|
||||||
* 移动端也不用单独适配那排 chip。 */ ?>
|
|
||||||
<button type="button" class="sw-toggle" id="swToggle"
|
<button type="button" class="sw-toggle" id="swToggle"
|
||||||
aria-label="切换软件" aria-expanded="false" aria-controls="swPanel">
|
aria-label="切换软件" aria-expanded="false" aria-controls="swPanel">
|
||||||
<span></span><span></span><span></span>
|
<span></span><span></span><span></span>
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (count($swList) > 1): ?>
|
<?php /* 极简下拉:贴在按钮下方、右对齐,内容多高就多高。
|
||||||
<?php /* 遮罩:抽屉打开时盖住正文,点击即关。
|
参考家书 FamilyBook 顶栏那个小浮窗的做法。 */ ?>
|
||||||
用户要求「把下面的内容全部盖住」,加一层浅遮罩最直观。 */ ?>
|
<div class="sw-panel" id="swPanel" hidden>
|
||||||
<div class="sw-mask" id="swMask" hidden></div>
|
|
||||||
<div class="sw-panel" id="swPanel" hidden>
|
|
||||||
<div class="wrap">
|
|
||||||
<div class="sw-panel-hd">选择软件</div>
|
|
||||||
<div class="sw-panel-list">
|
<div class="sw-panel-list">
|
||||||
<?php foreach ($swList as $s): ?>
|
<?php foreach ($swList as $s): ?>
|
||||||
<?php
|
<?php
|
||||||
@@ -114,14 +106,14 @@ function fb_layout_head($title, array $opt = []) {
|
|||||||
if ($href === '') { $href = '?sw=' . urlencode((string)$s['slug']); }
|
if ($href === '') { $href = '?sw=' . urlencode((string)$s['slug']); }
|
||||||
?>
|
?>
|
||||||
<a class="sw-item<?php echo $on ? ' on' : ''; ?>" href="<?php echo e($href); ?>">
|
<a class="sw-item<?php echo $on ? ' on' : ''; ?>" href="<?php echo e($href); ?>">
|
||||||
<?php echo fb_software_badge($s, 'md'); ?>
|
<?php echo fb_software_badge($s, 'sm'); ?>
|
||||||
<?php if ($on): ?><span class="sw-item-cur">当前</span><?php endif; ?>
|
<?php if ($on): ?><span class="sw-item-cur">当前</span><?php endif; ?>
|
||||||
</a>
|
</a>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<?php endif; ?>
|
||||||
<?php endif; ?>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main class="wrap">
|
<main class="wrap">
|
||||||
<?php
|
<?php
|
||||||
|
|||||||
Reference in New Issue
Block a user