追加 Hosts、路径撞名拦截、连接页表头随行滚动、GEO 借内核通路
设置页内核参数加 hosts 行,铅笔进弹窗编辑(顶层 hosts: 键, config.go:454,值单个或数组 parseHosts config.go:1131)。弹窗里随便改、 保存才一次性提交——写配置有几秒的校验重载,逐行失焦提交会把等待塞进 每次输入。没动过的行原样写回,数组不被重整形。 订阅和规则组新增时查缓存路径撞名:路径由名字净化而来,「订阅 A」和 「订阅/A」是两个名字、同一个文件,名字查重挡不住,会互相覆盖缓存。 纯函数在 shared 带测试,页面提交前拦。 连接页表头挪进滚动容器加 sticky:.scroller 声明 overflow-y 后按规范 连带拿到横向滚动上下文,表头留在外面时窄屏横向滚动两者脱节。表头 40px 的窗口偏移由 overscan 8 行吸收,不配 scrollMargin。 GEO 单文件下载先借内核的混合端口出去再退直连——内核自己更新 GEO 走 的是它的分流,Patchbay 直连 GitHub 不一定通。ECharts 拆独立 chunk, 业务代码改动不再让这 500KB 跟着换 hash。
This commit is contained in:
@@ -175,30 +175,32 @@ async function closeAllConfirmed() {
|
||||
<p v-if="rows.length === 0" class="note">没有主机匹配「{{ query.trim() }}」。</p>
|
||||
|
||||
<div v-else class="table">
|
||||
<div class="rowgrid thead">
|
||||
<span>主机</span>
|
||||
<span>规则</span>
|
||||
<span>出站链</span>
|
||||
<span class="num">上传</span>
|
||||
<button
|
||||
type="button"
|
||||
:class="['th num', sort === 'download' && 'is-on']"
|
||||
@click="sort = 'download'"
|
||||
>
|
||||
下载{{ sort === 'download' ? ' ↓' : '' }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="['th num', sort === 'duration' && 'is-on']"
|
||||
@click="sort = 'duration'"
|
||||
>
|
||||
持续时间{{ sort === 'duration' ? ' ↓' : '' }}
|
||||
</button>
|
||||
<span>入站</span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<!-- 表头在滚动容器里而不是外面:.scroller 声明 overflow-y 后按规范会连带
|
||||
拿到自己的横向滚动上下文,表头留在外面的话窄屏横向滚动时两者会脱节。
|
||||
竖直方向靠 sticky 钉住。 -->
|
||||
<div ref="scrollEl" class="scroller">
|
||||
<div class="rowgrid thead">
|
||||
<span>主机</span>
|
||||
<span>规则</span>
|
||||
<span>出站链</span>
|
||||
<span class="num">上传</span>
|
||||
<button
|
||||
type="button"
|
||||
:class="['th num', sort === 'download' && 'is-on']"
|
||||
@click="sort = 'download'"
|
||||
>
|
||||
下载{{ sort === 'download' ? ' ↓' : '' }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="['th num', sort === 'duration' && 'is-on']"
|
||||
@click="sort = 'duration'"
|
||||
>
|
||||
持续时间{{ sort === 'duration' ? ' ↓' : '' }}
|
||||
</button>
|
||||
<span>入站</span>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="spacer" :style="{ height: `${totalSize}px` }">
|
||||
<div
|
||||
v-for="vRow in virtualRows"
|
||||
@@ -367,12 +369,12 @@ async function closeAllConfirmed() {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* 窄屏不挤变形,横向滚动 */
|
||||
.table {
|
||||
border: 1px solid var(--c-border);
|
||||
border-radius: var(--r-md);
|
||||
background: var(--c-surface);
|
||||
overflow-x: auto;
|
||||
/* 只负责圆角裁切;横竖两个方向的滚动都归 .scroller 一个容器管 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rowgrid {
|
||||
@@ -386,9 +388,15 @@ async function closeAllConfirmed() {
|
||||
padding: 0 var(--s-4);
|
||||
}
|
||||
|
||||
/* 表头随内容横向滚动(同一个容器),竖直方向 sticky 钉在顶上。
|
||||
行是绝对定位的,要 z-index 才能压住;背景不透明才不会透出滚过的行 */
|
||||
.thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
height: 40px;
|
||||
border-bottom: 1px solid var(--c-border);
|
||||
background: var(--c-surface);
|
||||
color: var(--c-text-faint);
|
||||
font-size: var(--t-xs);
|
||||
}
|
||||
@@ -412,9 +420,11 @@ async function closeAllConfirmed() {
|
||||
}
|
||||
|
||||
.scroller {
|
||||
overflow-y: auto;
|
||||
overflow: auto;
|
||||
/* 表格最长不顶满一屏:留出头部和汇总条的位置,连接少时随内容缩短 */
|
||||
max-height: calc(100vh - 20rem);
|
||||
/* 表头占 40px,虚拟滚动的可见窗口按纯行高算会偏这 40px——overscan 8 行
|
||||
(384px)足以盖住,不必给虚拟器配 scrollMargin */
|
||||
}
|
||||
|
||||
.spacer {
|
||||
|
||||
Reference in New Issue
Block a user