fix title area

This commit is contained in:
Julian Freeman
2026-04-04 19:51:06 -04:00
parent 886f513b5d
commit ff238eb534
3 changed files with 170 additions and 130 deletions

View File

@@ -57,7 +57,7 @@
width: 240px;
background-color: var(--sidebar-bg);
border-right: 1px solid var(--border-color);
padding: 40px 20px;
padding: 40px 20px 5px 20px;
display: flex;
flex-direction: column;
}

View File

@@ -1,69 +1,74 @@
<template>
<main class="content">
<header class="content-header">
<div class="header-left">
<h1>装机必备</h1>
<span v-if="store.essentialsVersion" class="version-badge">版本: {{ store.essentialsVersion }}</span>
</div>
<div class="header-actions">
<button
@click="store.syncDataIfNeeded(true)"
class="secondary-btn action-btn"
:disabled="store.loading || store.isBusy"
>
<span class="icon" :class="{ 'spinning': store.loading }">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 2v6h-6"></path>
<path d="M3 12a9 9 0 0 1 15-6.7L21 8"></path>
<path d="M3 22v-6h6"></path>
<path d="M21 12a9 9 0 0 1-15 6.7L3 16"></path>
</svg>
</span>
{{ store.loading ? '正在刷新...' : '刷新状态' }}
</button>
<button
@click="installSelected"
class="primary-btn action-btn"
:disabled="store.loading || store.isBusy || store.selectedEssentialIds.length === 0"
>
安装所选 ({{ store.selectedEssentialIds.length }})
</button>
</div>
</header>
<!-- 固定标头区域 -->
<div class="sticky-header">
<header class="content-header">
<div class="header-left">
<h1>装机必备</h1>
<span v-if="store.essentialsVersion" class="version-badge">版本: {{ store.essentialsVersion }}</span>
</div>
<div class="header-actions">
<button
@click="store.syncDataIfNeeded(true)"
class="secondary-btn action-btn"
:disabled="store.loading || store.isBusy"
>
<span class="icon" :class="{ 'spinning': store.loading }">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 2v6h-6"></path>
<path d="M3 12a9 9 0 0 1 15-6.7L21 8"></path>
<path d="M3 22v-6h6"></path>
<path d="M21 12a9 9 0 0 1-15 6.7L3 16"></path>
</svg>
</span>
{{ store.loading ? '正在刷新...' : '刷新状态' }}
</button>
<button
@click="installSelected"
class="primary-btn action-btn"
:disabled="store.loading || store.isBusy || store.selectedEssentialIds.length === 0"
>
安装所选 ({{ store.selectedEssentialIds.length }})
</button>
</div>
</header>
<!-- 批量选择控制栏 -->
<div class="selection-toolbar" v-if="selectableItems.length > 0">
<div class="toolbar-left">
<span class="selection-count">已选 {{ store.selectedEssentialIds.length }} / {{ selectableItems.length }} </span>
</div>
<div class="toolbar-actions">
<button @click="store.selectAll('essential')" class="text-btn" :disabled="store.isBusy">全选</button>
<div class="divider"></div>
<button @click="store.deselectAll('essential')" class="text-btn" :disabled="store.isBusy">取消</button>
<div class="divider"></div>
<button @click="store.invertSelection('essential')" class="text-btn" :disabled="store.isBusy">反选</button>
<!-- 批量选择控制栏 -->
<div class="selection-toolbar" v-if="selectableItems.length > 0">
<div class="toolbar-left">
<span class="selection-count">已选 {{ store.selectedEssentialIds.length }} / {{ selectableItems.length }} </span>
</div>
<div class="toolbar-actions">
<button @click="store.selectAll('essential')" class="text-btn" :disabled="store.isBusy">全选</button>
<div class="divider"></div>
<button @click="store.deselectAll('essential')" class="text-btn" :disabled="store.isBusy">取消</button>
<div class="divider"></div>
<button @click="store.invertSelection('essential')" class="text-btn" :disabled="store.isBusy">反选</button>
</div>
</div>
</div>
<div v-if="store.loading && store.mergedEssentials.length === 0" class="loading-state">
<div class="spinner"></div>
<p>正在读取必备软件列表...</p>
</div>
<div v-else class="software-list">
<SoftwareCard
v-for="item in store.mergedEssentials"
:key="item.id"
:software="item"
:action-label="item.actionLabel"
:selectable="true"
:is-selected="store.selectedEssentialIds.includes(item.id)"
:disabled="store.isBusy"
@install="store.install"
@toggle-select="store.toggleSelection($event, 'essential')"
@toggle-post-install="store.togglePostInstallPref"
/>
<!-- 可滚动内容区域 -->
<div class="scroll-content">
<div v-if="store.loading && store.mergedEssentials.length === 0" class="loading-state">
<div class="spinner"></div>
<p>正在读取必备软件列表...</p>
</div>
<div v-else class="software-list">
<SoftwareCard
v-for="item in store.mergedEssentials"
:key="item.id"
:software="item"
:action-label="item.actionLabel"
:selectable="true"
:is-selected="store.selectedEssentialIds.includes(item.id)"
:disabled="store.isBusy"
@install="store.install"
@toggle-select="store.toggleSelection($event, 'essential')"
@toggle-post-install="store.togglePostInstallPref"
/>
</div>
</div>
</main>
</template>
@@ -99,8 +104,23 @@ onMounted(() => {
<style scoped>
.content {
flex: 1;
padding: 40px 60px;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden; /* 关键:禁止最外层滚动 */
}
.sticky-header {
padding: 40px 60px 10px 60px;
background-color: #F5F5F7; /* 与 App.vue 背景色保持一致 */
z-index: 10;
flex-shrink: 0;
}
.scroll-content {
flex: 1;
overflow-y: auto;
padding: 10px 60px 40px 60px;
}
.content-header {
@@ -138,16 +158,16 @@ onMounted(() => {
align-items: center;
}
/* 批量选择工具栏 */
.selection-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 20px;
background-color: rgba(0, 0, 0, 0.02);
background-color: white; /* 这里的工具栏背景设为白色更清晰 */
border-radius: 12px;
margin-bottom: 20px;
margin-bottom: 10px;
border: 1px solid rgba(0, 0, 0, 0.03);
box-shadow: 0 2px 8px rgba(0,0,0,0.02);
}
.selection-count {

View File

@@ -1,73 +1,78 @@
<template>
<main class="content">
<header class="content-header">
<div class="header-left">
<h1>软件更新</h1>
</div>
<div class="header-actions">
<button
@click="store.fetchUpdates"
class="secondary-btn action-btn"
:disabled="store.loading || store.isBusy"
>
<span class="icon" :class="{ 'spinning': store.loading }">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 2v6h-6"></path>
<path d="M3 12a9 9 0 0 1 15-6.7L21 8"></path>
<path d="M3 22v-6h6"></path>
<path d="M21 12a9 9 0 0 1-15 6.7L3 16"></path>
</svg>
</span>
{{ store.loading ? '正在检查...' : '检查更新' }}
</button>
<button
@click="updateSelected"
class="primary-btn action-btn"
:disabled="store.selectedUpdateIds.length === 0 || store.loading || store.isBusy"
>
更新所选 ({{ store.selectedUpdateIds.length }})
</button>
</div>
</header>
<!-- 固定标头区域 -->
<div class="sticky-header">
<header class="content-header">
<div class="header-left">
<h1>软件更新</h1>
</div>
<div class="header-actions">
<button
@click="store.fetchUpdates"
class="secondary-btn action-btn"
:disabled="store.loading || store.isBusy"
>
<span class="icon" :class="{ 'spinning': store.loading }">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 2v6h-6"></path>
<path d="M3 12a9 9 0 0 1 15-6.7L21 8"></path>
<path d="M3 22v-6h6"></path>
<path d="M21 12a9 9 0 0 1-15 6.7L3 16"></path>
</svg>
</span>
{{ store.loading ? '正在检查...' : '检查更新' }}
</button>
<button
@click="updateSelected"
class="primary-btn action-btn"
:disabled="store.selectedUpdateIds.length === 0 || store.loading || store.isBusy"
>
更新所选 ({{ store.selectedUpdateIds.length }})
</button>
</div>
</header>
<!-- 批量选择控制栏 -->
<div class="selection-toolbar" v-if="store.sortedUpdates.length > 0">
<div class="toolbar-left">
<span class="selection-count">已选 {{ store.selectedUpdateIds.length }} / {{ store.sortedUpdates.length }} </span>
</div>
<div class="toolbar-actions">
<button @click="store.selectAll('update')" class="text-btn" :disabled="store.isBusy">全选</button>
<div class="divider"></div>
<button @click="store.deselectAll('update')" class="text-btn" :disabled="store.isBusy">取消</button>
<div class="divider"></div>
<button @click="store.invertSelection('update')" class="text-btn" :disabled="store.isBusy">反选</button>
<!-- 批量选择控制栏 -->
<div class="selection-toolbar" v-if="store.sortedUpdates.length > 0">
<div class="toolbar-left">
<span class="selection-count">已选 {{ store.selectedUpdateIds.length }} / {{ store.sortedUpdates.length }} </span>
</div>
<div class="toolbar-actions">
<button @click="store.selectAll('update')" class="text-btn" :disabled="store.isBusy">全选</button>
<div class="divider"></div>
<button @click="store.deselectAll('update')" class="text-btn" :disabled="store.isBusy">取消</button>
<div class="divider"></div>
<button @click="store.invertSelection('update')" class="text-btn" :disabled="store.isBusy">反选</button>
</div>
</div>
</div>
<div v-if="store.loading && store.updates.length === 0" class="loading-state">
<div class="spinner"></div>
<p>正在使用 Winget 扫描可用的更新...</p>
</div>
<!-- 可滚动内容区域 -->
<div class="scroll-content">
<div v-if="store.loading && store.updates.length === 0" class="loading-state">
<div class="spinner"></div>
<p>正在使用 Winget 扫描可用的更新...</p>
</div>
<div v-else-if="store.updates.length === 0 && !store.loading" class="empty-state">
<span class="empty-icon"></span>
<p>所有软件已是最新版本</p>
</div>
<div v-else class="software-list">
<SoftwareCard
v-for="item in store.sortedUpdates"
:key="item.id"
:software="item"
:action-label="item.actionLabel"
:selectable="true"
:is-selected="store.selectedUpdateIds.includes(item.id)"
:disabled="store.isBusy"
@install="store.install"
@toggle-select="store.toggleSelection($event, 'update')"
@toggle-post-install="store.togglePostInstallPref"
/>
<div v-else-if="store.updates.length === 0 && !store.loading" class="empty-state">
<span class="empty-icon"></span>
<p>所有软件已是最新版本</p>
</div>
<div v-else class="software-list">
<SoftwareCard
v-for="item in store.sortedUpdates"
:key="item.id"
:software="item"
:action-label="item.actionLabel"
:selectable="true"
:is-selected="store.selectedUpdateIds.includes(item.id)"
:disabled="store.isBusy"
@install="store.install"
@toggle-select="store.toggleSelection($event, 'update')"
@toggle-post-install="store.togglePostInstallPref"
/>
</div>
</div>
</main>
</template>
@@ -100,8 +105,23 @@ onMounted(() => {
<style scoped>
.content {
flex: 1;
padding: 40px 60px;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden; /* 关键:禁止最外层滚动 */
}
.sticky-header {
padding: 40px 60px 10px 60px;
background-color: #F5F5F7;
z-index: 10;
flex-shrink: 0;
}
.scroll-content {
flex: 1;
overflow-y: auto;
padding: 10px 60px 40px 60px;
}
.content-header {
@@ -123,16 +143,16 @@ onMounted(() => {
align-items: center;
}
/* 批量选择工具栏 */
.selection-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 20px;
background-color: rgba(0, 0, 0, 0.02);
background-color: white;
border-radius: 12px;
margin-bottom: 20px;
margin-bottom: 10px;
border: 1px solid rgba(0, 0, 0, 0.03);
box-shadow: 0 2px 8px rgba(0,0,0,0.02);
}
.selection-count {