Browse Source

refactor(theme): tokenize public layout, header and content views

main
Mohan 1 month ago
parent
commit
30706bd2d8
  1. 93
      src/App.vue
  2. 25
      src/views/ArticleDetailView.vue
  3. 7
      src/views/HomeView.vue
  4. 8
      src/views/NotFoundView.vue
  5. 27
      src/views/SearchView.vue

93
src/App.vue

@ -1,9 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useTheme } from '@/composables/useTheme'
const auth = useAuthStore() const auth = useAuthStore()
const router = useRouter() const router = useRouter()
const theme = useTheme()
async function handleLogout() { async function handleLogout() {
try { try {
@ -27,9 +29,19 @@ async function handleLogout() {
<a v-if="auth.isLoggedIn" href="#" @click.prevent="handleLogout">退出</a> <a v-if="auth.isLoggedIn" href="#" @click.prevent="handleLogout">退出</a>
<router-link v-else to="/login">登录</router-link> <router-link v-else to="/login">登录</router-link>
</nav> </nav>
<button
class="theme-toggle"
type="button"
:title="theme.isDark ? '切至天青昼' : '切至黛墨夜'"
@click="theme.toggle()"
>{{ theme.isDark ? '昼' : '夜' }}</button>
</header> </header>
<main class="app-main"> <main class="app-main">
<router-view />
<router-view v-slot="{ Component }">
<transition name="page" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</main> </main>
</div> </div>
</template> </template>
@ -39,42 +51,85 @@ async function handleLogout() {
box-sizing: border-box; box-sizing: border-box;
} }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'PingFang SC',
'Microsoft YaHei', sans-serif;
background-color: #f5f6f8;
color: #24292f;
line-height: 1.6;
}
.app-header { .app-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
gap: 20px;
padding: 0 24px; padding: 0 24px;
height: 56px;
background-color: #1f2430;
color: #fff;
height: 60px;
background-color: var(--ac-header-bg);
color: var(--ac-header-text);
border-bottom: 1px solid var(--ac-border);
transition: background-color var(--song-dur-2) var(--song-ease), color var(--song-dur-2) var(--song-ease);
} }
.app-header .brand { .app-header .brand {
color: #fff;
display: flex;
align-items: center;
height: 100%;
margin-right: auto;
color: var(--ac-header-text);
font-family: var(--song-font-display);
font-size: 18px; font-size: 18px;
font-weight: 700;
font-weight: 400;
letter-spacing: 0.06em;
text-decoration: none; text-decoration: none;
} }
.nav-links {
display: flex;
align-items: center;
height: 100%;
gap: 6px;
}
.nav-links a, .nav-links a,
.nav-links .username { .nav-links .username {
margin-left: 18px;
color: #cfd3dc;
color: var(--ac-header-muted);
text-decoration: none; text-decoration: none;
font-size: 14px; font-size: 14px;
} }
.nav-links a {
display: inline-flex;
align-items: center;
height: 100%;
padding: 0 12px;
color: var(--ac-header-muted);
}
.nav-links a:hover { .nav-links a:hover {
color: #fff;
color: var(--ac-header-text);
}
.nav-links a.router-link-active {
color: var(--ac-header-text);
background: var(--ac-header-active-bg);
}
.nav-links .username {
margin-left: 12px;
}
.theme-toggle {
width: 34px;
height: 34px;
flex-shrink: 0;
border: 1px solid var(--ac-border);
border-radius: var(--ac-radius-m);
background: transparent;
color: var(--ac-header-muted);
font-family: var(--song-font-serif);
font-size: 15px;
letter-spacing: 0.1em;
cursor: pointer;
transition: color var(--song-dur-1) var(--song-ease), border-color var(--song-dur-1) var(--song-ease), background var(--song-dur-1) var(--song-ease);
}
.theme-toggle:hover {
color: var(--ac-header-text);
border-color: var(--ac-header-muted);
background: var(--ac-header-active-bg);
} }
.app-main { .app-main {

25
src/views/ArticleDetailView.vue

@ -72,26 +72,29 @@ watch(slug, load, { immediate: true })
<style scoped> <style scoped>
.article { .article {
background: #fff;
border: 1px solid #e5e5ea;
border-radius: 8px;
background: var(--ac-bg-card);
border: 1px solid var(--ac-border);
border-radius: var(--song-radius-l);
padding: 24px; padding: 24px;
} }
.detail-title { .detail-title {
font-family: var(--song-font-display);
font-weight: 400;
letter-spacing: 0.02em;
margin: 0 0 12px; margin: 0 0 12px;
font-size: 26px; font-size: 26px;
} }
.detail-summary { .detail-summary {
color: #5c6470;
color: var(--ac-text-secondary);
font-size: 15px; font-size: 15px;
} }
.detail-meta { .detail-meta {
display: flex; display: flex;
gap: 20px; gap: 20px;
color: #8a93a3;
color: var(--ac-text-disabled);
font-size: 13px; font-size: 13px;
margin: 12px 0; margin: 12px 0;
} }
@ -104,23 +107,23 @@ watch(slug, load, { immediate: true })
display: inline-block; display: inline-block;
margin-right: 8px; margin-right: 8px;
padding: 2px 10px; padding: 2px 10px;
background: #eef3ff;
color: #3b5bdb;
border-radius: 12px;
background: var(--ac-primary-bg);
color: var(--ac-primary-strong);
border-radius: var(--song-radius-m);
font-size: 12px; font-size: 12px;
} }
.article-content { .article-content {
border-top: 1px solid #eee;
border-top: 1px solid var(--ac-border);
padding-top: 20px; padding-top: 20px;
word-break: break-word; word-break: break-word;
} }
.hint-text { .hint-text {
color: #8a93a3;
color: var(--ac-text-disabled);
} }
.error-text { .error-text {
color: #d33c3c;
color: var(--ac-danger);
} }
</style> </style>

7
src/views/HomeView.vue

@ -77,15 +77,18 @@ onMounted(load)
} }
.page-title { .page-title {
font-family: var(--song-font-display);
font-size: 24px; font-size: 24px;
font-weight: 400;
letter-spacing: 0.04em;
margin: 0 0 20px; margin: 0 0 20px;
} }
.error-text { .error-text {
color: #d33c3c;
color: var(--ac-danger);
} }
.hint-text { .hint-text {
color: #8a93a3;
color: var(--ac-text-disabled);
} }
</style> </style>

8
src/views/NotFoundView.vue

@ -18,17 +18,19 @@ setPageMeta({ title: '页面不存在' })
} }
.not-found h1 { .not-found h1 {
font-size: 48px;
font-family: var(--song-font-display);
font-size: 72px;
font-weight: 400;
margin: 0 0 12px; margin: 0 0 12px;
} }
.not-found p { .not-found p {
color: #5c6470;
color: var(--ac-text-secondary);
margin: 0 0 20px; margin: 0 0 20px;
} }
.not-found a { .not-found a {
color: #3b82f6;
color: var(--ac-primary);
text-decoration: none; text-decoration: none;
} }
</style> </style>

27
src/views/SearchView.vue

@ -95,25 +95,38 @@ onMounted(() => {
.search-input { .search-input {
flex: 1; flex: 1;
padding: 10px 14px; padding: 10px 14px;
border: 1px solid #d3d8e0;
border-radius: 6px;
border: 1px solid var(--ac-input-border);
border-radius: var(--song-radius-l);
font-size: 14px; font-size: 14px;
background: var(--ac-bg-card);
color: var(--ac-text);
transition: border-color var(--song-dur-1) var(--song-ease);
}
.search-input:focus {
border-color: var(--ac-primary);
outline: none;
} }
.search-btn { .search-btn {
padding: 10px 22px; padding: 10px 22px;
border: none; border: none;
border-radius: 6px;
background: #3b82f6;
color: #fff;
border-radius: var(--song-radius-l);
background: var(--ac-primary-strong);
color: var(--ac-primary-text);
cursor: pointer; cursor: pointer;
transition: opacity var(--song-dur-1) var(--song-ease);
}
.search-btn:hover {
opacity: 0.9;
} }
.error-text { .error-text {
color: #d33c3c;
color: var(--ac-danger);
} }
.hint-text { .hint-text {
color: #8a93a3;
color: var(--ac-text-disabled);
} }
</style> </style>
Loading…
Cancel
Save