feat: 统一侧栏折叠动画并收口拆解页
This commit is contained in:
+37
-29
@@ -51,8 +51,8 @@ const narrowMedia = window.matchMedia(NARROW_NAV)
|
||||
const narrow = ref(narrowMedia.matches)
|
||||
const collapsed = ref(narrow.value)
|
||||
const settingsOpen = ref(false)
|
||||
/** 窄屏折叠只裁切文字,不把菜单收成图标,避免推挤动画时错位。 */
|
||||
const menuCollapsed = computed(() => !narrow.value && collapsed.value)
|
||||
/** 折叠只裁切文字,不把菜单收成图标,宽屏与窄屏共用同一套宽度动画。 */
|
||||
const menuCollapsed = false
|
||||
/** 当前视口下的折叠侧栏宽度。 */
|
||||
const siderCollapsedWidth = computed(() => (narrow.value ? SIDER_COLLAPSED_WIDTH_NARROW : SIDER_COLLAPSED_WIDTH))
|
||||
/** 当前视口下的展开侧栏宽度。 */
|
||||
@@ -163,14 +163,14 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
:style="{ width: (collapsed ? siderCollapsedWidth : siderExpandedWidth) + 'px' }"
|
||||
aria-label="主导航"
|
||||
>
|
||||
<!-- 以下是侧栏品牌:折叠仅图标,展开图标加文字 -->
|
||||
<!-- 以下是侧栏品牌:折叠裁切文字,图标位置保持不变 -->
|
||||
<RouterLink
|
||||
to="/projects"
|
||||
class="admin-brand"
|
||||
:class="{ 'is-collapsed': collapsed }"
|
||||
aria-label="短剧工作台首页"
|
||||
>
|
||||
<Clapperboard :size="narrow ? NAV_ICON_SIZE : 24" /><span v-if="!collapsed"
|
||||
<Clapperboard :size="narrow ? NAV_ICON_SIZE : 24" /><span
|
||||
>短剧工作台<small>SHORT DRAMA STUDIO</small></span
|
||||
>
|
||||
</RouterLink>
|
||||
@@ -186,7 +186,7 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
:root-indent="navIconIndent"
|
||||
/>
|
||||
</NScrollbar>
|
||||
<!-- 以下是侧栏底部后端连接:折叠仅图标,展开图标加文字 -->
|
||||
<!-- 以下是侧栏底部后端连接:折叠裁切文字,图标位置保持不变 -->
|
||||
<footer class="admin-sider-footer">
|
||||
<NTooltip placement="right" :disabled="!collapsed">
|
||||
<template #trigger>
|
||||
@@ -199,7 +199,7 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
@click="openSettings"
|
||||
>
|
||||
<template #icon><Settings2 :size="18" /></template>
|
||||
<span v-if="!collapsed">后端连接</span>
|
||||
<span class="admin-settings-label">后端连接</span>
|
||||
</NButton>
|
||||
</template>
|
||||
后端连接
|
||||
@@ -272,37 +272,57 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
}
|
||||
.admin-sider {
|
||||
@apply flex flex-col h-full w-[208px] shrink-0 overflow-hidden bg-(--app-subtle);
|
||||
transition: width 0.32s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
.admin-sider.is-collapsed {
|
||||
@apply w-16;
|
||||
}
|
||||
.admin-nav-scroll.n-scrollbar {
|
||||
@apply flex-1 min-h-0;
|
||||
min-width: var(--admin-sider-expanded);
|
||||
}
|
||||
.admin-sider .n-menu-item-content-header {
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.admin-sider.is-collapsed .n-menu-item-content-header {
|
||||
opacity: 0;
|
||||
}
|
||||
.admin-sider-footer {
|
||||
@apply w-full shrink-0 overflow-hidden;
|
||||
padding-block: 8px max(8px, env(safe-area-inset-bottom));
|
||||
}
|
||||
.n-button.admin-settings-button {
|
||||
@apply justify-start h-11 w-full px-5;
|
||||
@apply justify-start h-11 w-full overflow-hidden px-5;
|
||||
}
|
||||
.n-button.admin-settings-button.is-collapsed {
|
||||
@apply justify-center px-0;
|
||||
.n-button.admin-settings-button .n-button__icon {
|
||||
@apply shrink-0;
|
||||
}
|
||||
.admin-settings-label {
|
||||
@apply whitespace-nowrap;
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.admin-sider.is-collapsed .admin-settings-label {
|
||||
opacity: 0;
|
||||
}
|
||||
.admin-sider .n-menu .n-menu-item-content::before {
|
||||
@apply left-0 right-0 rounded-none;
|
||||
}
|
||||
.admin-brand {
|
||||
@apply flex shrink-0 items-center gap-3 h-[74px] py-0 px-5 text-ink whitespace-nowrap;
|
||||
}
|
||||
.admin-brand.is-collapsed {
|
||||
@apply justify-center px-0;
|
||||
@apply flex shrink-0 items-center gap-3 h-[74px] overflow-hidden py-0 px-5 text-ink whitespace-nowrap;
|
||||
min-width: var(--admin-sider-expanded);
|
||||
}
|
||||
.admin-brand > svg {
|
||||
@apply shrink-0;
|
||||
}
|
||||
.admin-brand span {
|
||||
@apply text-[15px] font-semibold;
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.admin-sider.is-collapsed .admin-brand span {
|
||||
opacity: 0;
|
||||
}
|
||||
.admin-brand small {
|
||||
@apply block text-[8px] tracking-[1px] font-normal text-muted mt-[3px];
|
||||
@@ -319,7 +339,6 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
@media (max-width: 800px) {
|
||||
.admin-sider {
|
||||
overflow: hidden;
|
||||
transition: width 0.32s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
/* 主栏宽度锁在折叠态剩余空间,侧栏变宽时把主栏推到右侧而不是压窄。 */
|
||||
.admin-layout > .admin-main {
|
||||
@@ -328,21 +347,8 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
min-width: calc(100% - var(--admin-sider-collapsed));
|
||||
max-width: calc(100% - var(--admin-sider-collapsed));
|
||||
}
|
||||
.admin-nav-scroll.n-scrollbar {
|
||||
min-width: var(--admin-sider-expanded);
|
||||
}
|
||||
.admin-brand {
|
||||
@apply h-12 w-full overflow-hidden;
|
||||
}
|
||||
.admin-sider:not(.is-collapsed) .admin-brand {
|
||||
@apply justify-start px-3.5;
|
||||
}
|
||||
.admin-sider .n-menu-item-content-header {
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.admin-sider.is-collapsed .n-menu-item-content-header {
|
||||
opacity: 0;
|
||||
@apply h-12 justify-start px-3.5;
|
||||
}
|
||||
.admin-topbar {
|
||||
@apply py-0 px-2.5;
|
||||
@@ -350,7 +356,9 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.admin-sider,
|
||||
.admin-sider .n-menu-item-content-header {
|
||||
.admin-sider .n-menu-item-content-header,
|
||||
.admin-brand span,
|
||||
.admin-settings-label {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,19 +54,22 @@ const open = defineModel<boolean>('open', { default: false })
|
||||
@reference "../../styles/styles.css";
|
||||
.workspace-tools-drawer .n-drawer-header,
|
||||
.workspace-tools-drawer .n-drawer-footer {
|
||||
@apply bg-(--app-subtle);
|
||||
@apply bg-(--app-subtle) p-4;
|
||||
}
|
||||
.workspace-tools-drawer .n-drawer-body {
|
||||
@apply bg-(--app-body);
|
||||
@apply bg-(--app-body) p-0;
|
||||
}
|
||||
.workspace-tools-drawer {
|
||||
--n-header-padding: 16px;
|
||||
--n-body-padding: 16px;
|
||||
--n-footer-padding: 16px;
|
||||
@apply max-w-full;
|
||||
}
|
||||
.workspace-tools-heading {
|
||||
@apply flex items-center justify-between gap-4 w-full text-[15px];
|
||||
}
|
||||
.workspace-tools-body {
|
||||
@apply p-5;
|
||||
@apply p-4;
|
||||
}
|
||||
.workspace-tools-body .storyboard-controls,
|
||||
.workspace-tools-body .production-controls {
|
||||
@@ -84,9 +87,4 @@ const open = defineModel<boolean>('open', { default: false })
|
||||
.workspace-tools-body .production-pipeline {
|
||||
@apply grid-cols-[repeat(auto-fit,_minmax(min(100%,_230px),_1fr))];
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.workspace-tools-body {
|
||||
@apply p-3.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -145,10 +145,7 @@ function exportResult() {
|
||||
>抽取 {{ summary.completed }} / {{ summary.total }} · 失败 {{ summary.failed }}</span
|
||||
>
|
||||
<span>主体 {{ subjects.length }} · 分镜剧集 {{ shots.length }} / {{ plans.length }}</span>
|
||||
</div>
|
||||
<div v-if="snapshot || project?.episodes.length" class="toolbar-actions">
|
||||
<StatusBadge
|
||||
v-if="snapshot"
|
||||
:status="execution?.status || 'unknown'"
|
||||
:label="
|
||||
execution?.status === 'completed'
|
||||
@@ -158,12 +155,9 @@ function exportResult() {
|
||||
: '执行状态待确认'
|
||||
"
|
||||
/>
|
||||
<WorkspaceTools
|
||||
v-if="project?.episodes.length"
|
||||
v-model:open="toolsOpen"
|
||||
title="拆解设置与恢复"
|
||||
label="拆解设置与恢复"
|
||||
>
|
||||
</div>
|
||||
<div v-if="project?.episodes.length" class="toolbar-actions">
|
||||
<WorkspaceTools v-model:open="toolsOpen" title="拆解设置与恢复" label="拆解设置与恢复">
|
||||
<section class="panel breakdown-settings-panel p-5 lg:p-6">
|
||||
<div class="mb-5 flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
@@ -423,14 +417,14 @@ function exportResult() {
|
||||
<!-- 所有结果标签共用有界的内容滚动;切换标签回到顶部,不继承上一列表的位置。 -->
|
||||
<NScrollbar :key="tab" class="panel-scroll breakdown-results-scroll">
|
||||
<template v-for="option in moduleOptions" :key="option.value"
|
||||
><div v-if="tab === option.value">
|
||||
><div v-if="tab === option.value" class="breakdown-result-pad">
|
||||
<SubjectList
|
||||
:project-id="project?.id"
|
||||
:subjects="subjects.filter(item => item.module === option.value)"
|
||||
:forms="forms"
|
||||
/></div
|
||||
></template>
|
||||
<div v-if="tab === 'storyboard'">
|
||||
<div v-if="tab === 'storyboard'" class="breakdown-result-pad">
|
||||
<StoryboardList :plans="plans" :episodes="shots">
|
||||
<template #actions>
|
||||
<RouterLink :to="`/projects/${project?.id}/storyboard`" class="text-button"
|
||||
@@ -439,7 +433,7 @@ function exportResult() {
|
||||
</template>
|
||||
</StoryboardList>
|
||||
</div>
|
||||
<div v-if="tab === 'tasks'" class="p-5">
|
||||
<div v-if="tab === 'tasks'" class="breakdown-result-pad">
|
||||
<NScrollbar x-scrollable v-if="snapshot?.tasks?.length" class="table-scroll">
|
||||
<NTable :bordered="false" :single-line="true" striped class="project-table"
|
||||
><thead>
|
||||
@@ -588,6 +582,9 @@ function exportResult() {
|
||||
.breakdown-results-scroll .n-scrollbar-content {
|
||||
@apply wrap-anywhere;
|
||||
}
|
||||
.breakdown-result-pad {
|
||||
@apply p-4;
|
||||
}
|
||||
.breakdown-results-section > .content-with-history.history-hidden {
|
||||
@apply grid-cols-[minmax(0,_1fr)] grid-rows-[minmax(0,_1fr)];
|
||||
}
|
||||
@@ -618,9 +615,6 @@ function exportResult() {
|
||||
.breakdown-workspace-page .toolbar-actions > * {
|
||||
@apply shrink-0;
|
||||
}
|
||||
.breakdown-workspace-page .toolbar-actions .n-tag {
|
||||
@apply hidden;
|
||||
}
|
||||
.breakdown-workspace-page .workspace-tools-label {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ const purposeLabels: Record<string, string> = {
|
||||
}
|
||||
}
|
||||
.breakdown-storyboard {
|
||||
@apply pt-4 px-5 pb-5;
|
||||
@apply min-w-0;
|
||||
}
|
||||
.breakdown-storyboard-toolbar {
|
||||
@apply flex items-center justify-between flex-wrap gap-y-3 gap-x-6 mb-4;
|
||||
|
||||
@@ -140,14 +140,6 @@ const filtered = computed(() =>
|
||||
.subject-details[open] summary svg {
|
||||
@apply rotate-180;
|
||||
}
|
||||
.subject-list {
|
||||
@apply p-5;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.subject-list {
|
||||
@apply p-7;
|
||||
}
|
||||
}
|
||||
.subject-list-toolbar {
|
||||
@apply flex flex-wrap items-center justify-start gap-y-2.5 gap-x-3 mb-5;
|
||||
}
|
||||
@@ -158,9 +150,6 @@ const filtered = computed(() =>
|
||||
@apply shrink-0 text-muted text-xs whitespace-nowrap;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
.subject-list {
|
||||
@apply p-3;
|
||||
}
|
||||
.subject-list-toolbar {
|
||||
@apply flex-nowrap gap-2 mb-3;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user