feat: 收紧移动端剧本列表与创作页顶栏

This commit is contained in:
GJ
2026-09-04 18:24:43 +08:00
parent db29367c69
commit 50b3461ebc
4 changed files with 130 additions and 46 deletions
+45 -12
View File
@@ -72,14 +72,17 @@ function exportScript() {
<WorkspacePage split class="script-workspace-page" <WorkspacePage split class="script-workspace-page"
><template #header ><template #header
><div class="stage-strip"> ><div class="stage-strip">
<div <!-- 以下是创作阶段进度 -->
v-for="(stage, index) in stages" <div class="stage-strip-track">
:key="stage.label" <div
class="stage-item" v-for="(stage, index) in stages"
:class="{ done: stage.done }" :key="stage.label"
> class="stage-item"
<Check v-if="stage.done" :size="15" /><Circle v-else :size="13" /><span>{{ stage.label }}</span :class="{ done: stage.done }"
><ArrowRight v-if="index < stages.length - 1" :size="13" class="stage-arrow" /> >
<Check v-if="stage.done" :size="15" /><Circle v-else :size="13" /><span>{{ stage.label }}</span
><ArrowRight v-if="index < stages.length - 1" :size="13" class="stage-arrow" />
</div>
</div></div </div></div
></template> ></template>
<div v-if="!complete" class="mt-5 flex flex-wrap items-center justify-between gap-4"> <div v-if="!complete" class="mt-5 flex flex-wrap items-center justify-between gap-4">
@@ -254,10 +257,13 @@ function exportScript() {
/* 本组件专属布局与 Naive 内部结构覆盖。 */ /* 本组件专属布局与 Naive 内部结构覆盖。 */
@reference "../../styles/styles.css"; @reference "../../styles/styles.css";
.stage-strip { .stage-strip {
@apply flex flex-wrap items-center gap-3.5; @apply flex items-center gap-3;
}
.stage-strip-track {
@apply flex min-w-0 flex-1 flex-wrap items-center gap-3.5;
} }
.stage-item { .stage-item {
@apply flex items-center gap-[7px] text-muted text-[11px]; @apply flex shrink-0 items-center gap-[7px] text-muted text-[11px];
} }
.stage-item.done { .stage-item.done {
@apply text-ink; @apply text-ink;
@@ -268,13 +274,40 @@ function exportScript() {
.json-view { .json-view {
@apply whitespace-pre-wrap wrap-anywhere p-4 bg-(--app-subtle) rounded-none font-mono text-[11px] leading-[1.9]; @apply whitespace-pre-wrap wrap-anywhere p-4 bg-(--app-subtle) rounded-none font-mono text-[11px] leading-[1.9];
} }
@media (max-width: 760px) { @media (max-width: 800px) {
.script-workspace-page .workspace-heading {
@apply py-2.5 px-3;
}
.stage-strip { .stage-strip {
@apply gap-[9px]; @apply gap-2;
}
.stage-strip-track {
@apply min-w-0 flex-1 flex-nowrap overflow-x-auto overflow-y-hidden overscroll-x-contain;
scrollbar-width: none;
mask-image: linear-gradient(to right, #000 calc(100% - 16px), transparent);
}
.stage-strip-track::-webkit-scrollbar {
display: none;
} }
.stage-arrow { .stage-arrow {
@apply ml-[3px]; @apply ml-[3px];
} }
.script-section .tabs-toolbar {
container-type: normal;
}
.script-section .workspace-tabs > .n-tabs-nav {
@apply flex-nowrap;
}
.script-section .workspace-tabs .n-tabs-nav-scroll-wrapper {
@apply min-w-0 flex-1;
flex-basis: auto;
}
.script-section .workspace-tabs .n-tabs-nav__suffix {
@apply w-auto pl-2;
}
.script-section .tabs-toolbar-actions {
@apply min-h-0 flex-nowrap;
}
} }
.script-section { .script-section {
@apply flex-1 flex flex-col min-h-0; @apply flex-1 flex flex-col min-h-0;
+80 -34
View File
@@ -67,39 +67,49 @@ function clearFilters() {
<template> <template>
<WorkspacePage split> <WorkspacePage split>
<template #header> <template #header>
<div class="page-heading project-index-heading"> <div class="project-index-chrome">
<div> <!-- 以下是标题与新建 -->
<h1>我的剧本</h1> <div class="page-heading project-index-heading">
<p class="page-description">从故事到镜头继续你的创作</p> <div>
<h1>我的剧本</h1>
<p class="page-description">从故事到镜头继续你的创作</p>
</div>
<CreateProjectDialog @created="openProject" />
</div>
<!-- 以下是状态筛选搜索与刷新 -->
<div class="project-index-toolbar">
<div class="project-status-filter-scroller">
<NRadioGroup
v-model:value="filter"
size="medium"
class="project-status-filters"
aria-label="项目状态筛选"
><NRadioButton v-for="item in filters" :key="item.value" :value="item.value">{{
item.label
}}</NRadioButton></NRadioGroup
>
</div>
<div class="project-index-search-row">
<NInput
v-model:value="search"
clearable
placeholder="搜索剧本"
class="project-search"
:input-props="{ 'aria-label': '搜索项目' }"
><template #prefix><Search :size="15" /></template
></NInput>
<NButton
class="icon-button"
:loading="query.loading.value"
aria-label="刷新项目"
title="刷新项目"
@click="query.refresh"
><template #icon><RefreshCw :size="16" /></template
></NButton>
</div>
</div> </div>
<CreateProjectDialog @created="openProject" />
</div> </div>
</template> </template>
<div class="project-index-toolbar">
<NRadioGroup v-model:value="filter" size="medium" class="project-status-filters" aria-label="项目状态筛选"
><NRadioButton v-for="item in filters" :key="item.value" :value="item.value">{{
item.label
}}</NRadioButton></NRadioGroup
>
<div class="flex items-center gap-2">
<NInput
v-model:value="search"
clearable
placeholder="搜索剧本"
class="project-search"
:input-props="{ 'aria-label': '搜索项目' }"
><template #prefix><Search :size="15" /></template
></NInput>
<NButton
class="icon-button"
:loading="query.loading.value"
aria-label="刷新项目"
title="刷新项目"
@click="query.refresh"
><template #icon><RefreshCw :size="16" /></template
></NButton>
</div>
</div>
<NAlert v-if="query.error.value" type="error" :show-icon="false" <NAlert v-if="query.error.value" type="error" :show-icon="false"
>{{ query.error.value }}<NButton text class="ml-3" @click="query.refresh">重新连接</NButton></NAlert >{{ query.error.value }}<NButton text class="ml-3" @click="query.refresh">重新连接</NButton></NAlert
> >
@@ -171,6 +181,9 @@ function clearFilters() {
.project-data-table .n-data-table-base-table { .project-data-table .n-data-table-base-table {
@apply flex-1 min-h-0; @apply flex-1 min-h-0;
} }
.project-index-chrome {
@apply flex flex-col gap-3;
}
.project-index-heading { .project-index-heading {
@apply m-0; @apply m-0;
} }
@@ -180,6 +193,9 @@ function clearFilters() {
.project-index-toolbar { .project-index-toolbar {
@apply flex items-center justify-between flex-wrap gap-3 mb-1; @apply flex items-center justify-between flex-wrap gap-3 mb-1;
} }
.project-index-search-row {
@apply flex min-w-0 items-center gap-2;
}
.project-search { .project-search {
@apply w-[240px]; @apply w-[240px];
} }
@@ -202,12 +218,42 @@ function clearFilters() {
.project-name-cell small { .project-name-cell small {
@apply text-muted mt-1 text-[11px]; @apply text-muted mt-1 text-[11px];
} }
@media (max-width: 760px) { @media (max-width: 800px) {
.project-search { .workspace-heading:has(.project-index-heading) {
@apply py-2.5 px-3;
}
.project-index-chrome {
@apply gap-2;
}
.project-index-heading {
@apply flex-row items-center gap-3;
}
.project-index-heading h1 {
@apply mt-0 text-lg;
}
.project-index-heading .page-description {
@apply hidden;
}
.project-index-heading .n-button {
@apply h-9 px-3 shrink-0;
}
.project-index-toolbar {
@apply flex-col items-stretch gap-2 mb-0;
}
.project-status-filter-scroller {
@apply w-full m-0;
}
.project-status-filters.n-radio-group {
@apply flex w-full flex-nowrap m-0 p-0;
}
.project-status-filters .n-radio-button {
@apply flex-1 min-w-0 px-2;
}
.project-index-search-row {
@apply w-full; @apply w-full;
} }
.project-index-toolbar .n-radio-group { .project-search {
@apply w-full; @apply w-auto min-w-0 flex-1;
} }
} }
</style> </style>
+1
View File
@@ -31,6 +31,7 @@ const STATIC: Record<string, string> = {
'inline-flex': 'display: inline-flex;', 'inline-flex': 'display: inline-flex;',
'flex-col': 'flex-direction: column;', 'flex-col': 'flex-direction: column;',
'flex-wrap': 'flex-wrap: wrap;', 'flex-wrap': 'flex-wrap: wrap;',
'flex-nowrap': 'flex-wrap: nowrap;',
'items-center': 'align-items: center;', 'items-center': 'align-items: center;',
'items-end': 'align-items: end;', 'items-end': 'align-items: end;',
'items-start': 'align-items: flex-start;', 'items-start': 'align-items: flex-start;',
+4
View File
@@ -208,6 +208,10 @@ describe('全站平面主题', () => {
/\.project-status-filters \.n-radio-button\s*\{[^}]*min-width:\s*76px;[^}]*padding-inline:\s*18px/ /\.project-status-filters \.n-radio-button\s*\{[^}]*min-width:\s*76px;[^}]*padding-inline:\s*18px/
) )
expect(css).toMatch(/\.project-status-filters\.n-radio-group\s*\{[^}]*flex-wrap:\s*wrap;[^}]*height:\s*auto;/) expect(css).toMatch(/\.project-status-filters\.n-radio-group\s*\{[^}]*flex-wrap:\s*wrap;[^}]*height:\s*auto;/)
expect(css).toContain('@media (max-width: 800px)')
expect(css).toMatch(/\.project-status-filters\.n-radio-group\s*\{[^}]*width:\s*100%;[^}]*flex-wrap:\s*nowrap/)
expect(css).toMatch(/\.project-status-filters \.n-radio-button\s*\{[^}]*flex:\s*1;[^}]*min-width:\s*0/)
expect(css).toMatch(/\.project-index-heading \.page-description\s*\{[^}]*display:\s*none/)
for (const file of [ for (const file of [
'src/App.vue', 'src/App.vue',
'src/components/ui/ThemeToggle.vue', 'src/components/ui/ThemeToggle.vue',