侧栏收拢时导航按钮跟着缩窄;配置区高度折叠动画;侧栏不再牵动页面内容

导航按钮宽度写死 159px 而不是 100%,百分比到像素之间没法插值,收拢时
按钮平滑缩成 36px 的方形贴住图标,高亮块不再被裁切出血。配置表单和
链式代理段的出现消失改成高度折叠(量出目标高度做插值,margin 换成
padding 否则那一截在动画之外瞬跳)。路由页「新增」按钮的文字不再跟着
侧栏密度隐藏:侧栏收拢是侧栏自己的事。
This commit is contained in:
2026-08-11 16:42:43 +08:00
parent 5a04968e17
commit 0ddefd0d5d
3 changed files with 56 additions and 5 deletions
+14 -1
View File
@@ -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 {
+40 -2
View File
@@ -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(
</section>
<!-- 跟着表单里的开关走:拨开立刻出现,拨掉(哪怕还没保存)立刻消失 -->
<Transition name="fold" @enter="foldEnter" @leave="foldLeave">
<section v-if="form.chain" class="block">
<h3>
链式代理
@@ -546,6 +580,7 @@ const complete = computed(
/>
</div>
</section>
</Transition>
<!-- 共用的组只能换选中,改不了它的成员来源:那是别人的组 -->
<section v-if="panel.shared && !locked" class="block">
@@ -560,6 +595,7 @@ const complete = computed(
<IconRight :class="['chev', open && 'is-open']" />
</button>
<Transition name="fold" @enter="foldEnter" @leave="foldLeave">
<div v-if="open" class="form">
<h4>代理</h4>
@@ -631,6 +667,7 @@ const complete = computed(
</button>
</div>
</div>
</Transition>
</section>
</template>
</div>
@@ -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 {
+2 -2
View File
@@ -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() {
</div>
</div>
<!-- 侧栏收拢是侧栏自己的事,页面内容不跟着变形 -->
<button class="add" type="button" :disabled="saving" @click="openAdd">
<IconAdd />
<span v-if="density === 'full'">新增</span>
<span>新增</span>
</button>
</header>