diff --git a/web/App.vue b/web/App.vue index 1c1e490..ff867c4 100644 --- a/web/App.vue +++ b/web/App.vue @@ -244,7 +244,8 @@ const current = computed(() => NAV.find((n) => n.key === active.value)) } .nav-item { - width: 100%; + /* 写死而不是 100%:百分比到像素之间没法插值,宽度动画会直接跳变 */ + width: 159px; display: flex; align-items: center; gap: var(--s-3); @@ -256,6 +257,18 @@ const current = computed(() => NAV.find((n) => n.key === active.value)) cursor: pointer; text-align: left; white-space: nowrap; + overflow: hidden; + /* width 跟侧栏同节奏,颜色沿用全局的 0.15s;transition 不叠加,这里要写全 */ + transition: + width 0.3s ease, + color 0.15s ease, + background-color 0.15s ease, + border-color 0.15s ease; +} + +/* 收拢后按钮缩成正方形贴着图标,高亮块不再被裁切:8 + 18 + 8 加两侧边框 = 36 */ +.is-compact .nav-item { + width: 36px; } .nav-item:hover { diff --git a/web/views/InboundDetail.vue b/web/views/InboundDetail.vue index c43a0b8..2fd42a1 100644 --- a/web/views/InboundDetail.vue +++ b/web/views/InboundDetail.vue @@ -160,6 +160,39 @@ function lockHeight() { el.style.overflow = 'hidden' } +/** + * v-if 内容的展开收起动画:高度在 0 和实际值之间过渡,结束后清掉内联样式。 + * 给配置表单和链式代理段用。auto 高度不可插值,只能量出来做。 + */ +function foldEnter(element: Element, done: () => void) { + const el = element as HTMLElement + el.style.height = '0' + el.style.overflow = 'hidden' + void el.offsetHeight + el.style.transition = 'height 0.15s ease' + el.style.height = `${String(el.scrollHeight)}px` + el.addEventListener( + 'transitionend', + () => { + el.style.transition = '' + el.style.height = '' + el.style.overflow = '' + done() + }, + { once: true }, + ) +} + +function foldLeave(element: Element, done: () => void) { + const el = element as HTMLElement + el.style.height = `${String(el.offsetHeight)}px` + el.style.overflow = 'hidden' + void el.offsetHeight + el.style.transition = 'height 0.15s ease' + el.style.height = '0' + el.addEventListener('transitionend', done, { once: true }) +} + function settleHeight() { const el = rootEl.value if (!el || !el.style.height) return @@ -507,6 +540,7 @@ const complete = computed( +

链式代理 @@ -546,6 +580,7 @@ const complete = computed( />

+
@@ -560,6 +595,7 @@ const complete = computed( +

代理

@@ -631,6 +667,7 @@ const complete = computed(
+
@@ -899,12 +936,13 @@ h3 { transform: rotate(90deg); } -/* 表单不套盒子,靠小标题和一条分割线分段(设计基调第 2 条:不要卡片套卡片) */ +/* 表单不套盒子,靠小标题和一条分割线分段(设计基调第 2 条:不要卡片套卡片)。 + 上方留白用 padding 而不是 margin:margin 在高度折叠动画之外,会瞬跳 */ .form { display: flex; flex-direction: column; gap: var(--s-3); - margin-top: var(--s-4); + padding-top: var(--s-4); } .form h4 { diff --git a/web/views/RouterPage.vue b/web/views/RouterPage.vue index d63c436..8c7268f 100644 --- a/web/views/RouterPage.vue +++ b/web/views/RouterPage.vue @@ -28,7 +28,6 @@ import type { ListenerFields } from '../api' import type { GroupView } from '../runtime' import { optimisticSelect, providerLines, reloadRuntime, runtime } from '../runtime' import { useDelayTest } from '../delay-test' -import { density } from '../prefs' import ConfirmDialog from '../components/ConfirmDialog.vue' import InboundList from '../components/InboundList.vue' import InboundDetail from './InboundDetail.vue' @@ -258,9 +257,10 @@ async function removeConfirmed() { +