Browse Source

fix(ux): improve comment spacing, merge admin titlebar into global header, fix dark mode editor

- CommentSection: add padding-top 8px to comment-item for spacing between author/time and top
- AdminLayout: remove sticky topbar (now merged into global App.vue header), clean up unused code
- App.vue: show page title for all routes with × separator after brand name, remove admin-route filter
- QuillEditor: use --ac-border-strong for toolbar/container borders, add toolbar button colors for dark mode
- ArticleEditorView: use --ac-border-strong for all borders, btn-cancel now light red with danger colors
main
Mohan 1 month ago
parent
commit
2379d220ce
  1. 8
      src/App.vue
  2. 1
      src/components/CommentSection.vue
  3. 21
      src/components/QuillEditor.vue
  4. 50
      src/views/admin/AdminLayout.vue
  5. 24
      src/views/admin/ArticleEditorView.vue

8
src/App.vue

@ -30,7 +30,7 @@ async function handleLogout() {
<div> <div>
<header class="app-header"> <header class="app-header">
<router-link to="/" class="brand">Mach-CMS</router-link> <router-link to="/" class="brand">Mach-CMS</router-link>
<span v-if="pageTitle && !isAdminRoute" class="header-page-title">{{ pageTitle }}</span>
<span v-if="pageTitle" class="header-page-title"><span class="header-title-sep">×</span>{{ pageTitle }}</span>
<nav class="nav-links"> <nav class="nav-links">
<router-link to="/">首页</router-link> <router-link to="/">首页</router-link>
<router-link to="/search">搜索</router-link> <router-link to="/search">搜索</router-link>
@ -89,6 +89,12 @@ async function handleLogout() {
color: var(--ac-header-text); color: var(--ac-header-text);
} }
.header-title-sep {
margin: 0 8px;
color: var(--ac-header-muted);
font-weight: 400;
}
.app-header .brand { .app-header .brand {
display: flex; display: flex;
align-items: center; align-items: center;

1
src/components/CommentSection.vue

@ -205,6 +205,7 @@ onMounted(() => {
.comment-item { .comment-item {
display: flex; display: flex;
gap: 16px; gap: 16px;
padding-top: 8px;
} }
.comment-item:not(:last-child) .comment-card { .comment-item:not(:last-child) .comment-card {

21
src/components/QuillEditor.vue

@ -87,13 +87,30 @@ watch(() => props.modelValue, (val) => {
line-height: 1.8; line-height: 1.8;
} }
.quill-editor :deep(.ql-toolbar) { .quill-editor :deep(.ql-toolbar) {
border-color: var(--ac-border, #d4c4b0);
border-color: var(--ac-border-strong, #d4c4b0);
background: var(--ac-surface, #faf8f5); background: var(--ac-surface, #faf8f5);
} }
.quill-editor :deep(.ql-container) { .quill-editor :deep(.ql-container) {
border-color: var(--ac-border, #d4c4b0);
border-color: var(--ac-border-strong, #d4c4b0);
} }
.quill-editor :deep(.ql-editor.ql-blank::before) { .quill-editor :deep(.ql-editor.ql-blank::before) {
color: var(--ac-text-disabled); color: var(--ac-text-disabled);
} }
.quill-editor :deep(.ql-toolbar button) {
color: var(--ac-text-secondary);
}
.quill-editor :deep(.ql-toolbar button:hover) {
color: var(--ac-text);
}
.quill-editor :deep(.ql-toolbar .ql-active) {
color: var(--ac-primary-ink);
}
.quill-editor :deep(.ql-toolbar .ql-picker-label),
.quill-editor :deep(.ql-toolbar .ql-picker-item) {
color: var(--ac-text-secondary);
}
.quill-editor :deep(.ql-toolbar .ql-picker-label:hover),
.quill-editor :deep(.ql-toolbar .ql-picker-item:hover) {
color: var(--ac-text);
}
</style> </style>

50
src/views/admin/AdminLayout.vue

@ -1,12 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
const auth = useAuthStore() const auth = useAuthStore()
const route = useRoute()
const pageTitle = computed(() => (route.meta.title as string) || '')
</script> </script>
<template> <template>
@ -19,18 +14,13 @@ const pageTitle = computed(() => (route.meta.title as string) || '')
<router-link v-if="auth.isAdmin" to="/admin/comments">评论审核</router-link> <router-link v-if="auth.isAdmin" to="/admin/comments">评论审核</router-link>
</nav> </nav>
</aside> </aside>
<div class="content-wrap">
<header class="topbar">
<span class="topbar-title">{{ pageTitle }}</span>
</header>
<main class="content">
<router-view v-slot="{ Component }">
<transition name="page" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</main>
</div>
<main class="content">
<router-view v-slot="{ Component }">
<transition name="page" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</main>
</div> </div>
</template> </template>
@ -70,33 +60,9 @@ const pageTitle = computed(() => (route.meta.title as string) || '')
color: var(--ac-header-text); color: var(--ac-header-text);
} }
.content-wrap {
.content {
flex: 1; flex: 1;
display: flex;
flex-direction: column;
min-width: 0; min-width: 0;
}
.topbar {
position: sticky;
top: 0;
z-index: 1000;
display: flex;
align-items: center;
padding: 0 20px;
height: 52px;
background: var(--ac-bg-card);
border-bottom: 1px solid var(--ac-border);
}
.topbar-title {
font-family: var(--song-font-serif);
font-size: 16px;
font-weight: 600;
color: var(--ac-text);
}
.content {
padding: 20px; padding: 20px;
} }
</style> </style>

24
src/views/admin/ArticleEditorView.vue

@ -234,7 +234,7 @@ onMounted(() => {
.title-input { .title-input {
width: 100%; width: 100%;
padding: 12px 16px; padding: 12px 16px;
border: 1px solid var(--ac-border);
border: 1px solid var(--ac-border-strong);
border-radius: var(--song-radius-l); border-radius: var(--song-radius-l);
font-family: 'Noto Serif SC', 'SimSun', 'STSong', serif; font-family: 'Noto Serif SC', 'SimSun', 'STSong', serif;
font-size: 22px; font-size: 22px;
@ -263,7 +263,7 @@ onMounted(() => {
.side-section { .side-section {
background: var(--ac-bg-card); background: var(--ac-bg-card);
border: 1px solid var(--ac-border);
border: 1px solid var(--ac-border-strong);
border-radius: var(--song-radius-l); border-radius: var(--song-radius-l);
padding: 16px; padding: 16px;
} }
@ -299,7 +299,7 @@ onMounted(() => {
.summary-input { .summary-input {
width: 100%; width: 100%;
padding: 8px 12px; padding: 8px 12px;
border: 1px solid var(--ac-input-border);
border: 1px solid var(--ac-border-strong);
border-radius: var(--song-radius-l); border-radius: var(--song-radius-l);
font-size: 13px; font-size: 13px;
line-height: 1.5; line-height: 1.5;
@ -323,7 +323,7 @@ onMounted(() => {
max-height: 120px; max-height: 120px;
object-fit: cover; object-fit: cover;
border-radius: var(--song-radius-m); border-radius: var(--song-radius-m);
border: 1px solid var(--ac-border);
border: 1px solid var(--ac-border-strong);
margin-bottom: 6px; margin-bottom: 6px;
} }
@ -344,15 +344,17 @@ onMounted(() => {
display: inline-block; display: inline-block;
padding: 8px 14px; padding: 8px 14px;
background: var(--ac-primary-bg); background: var(--ac-primary-bg);
border: 1px solid var(--ac-border-strong);
color: var(--ac-primary-ink); color: var(--ac-primary-ink);
border-radius: var(--song-radius-m); border-radius: var(--song-radius-m);
cursor: pointer; cursor: pointer;
font-size: 13px; font-size: 13px;
transition: background var(--song-dur-1) var(--song-ease);
transition: background var(--song-dur-1) var(--song-ease), border-color var(--song-dur-1) var(--song-ease);
} }
.upload-btn:hover { .upload-btn:hover {
background: var(--ac-active); background: var(--ac-active);
border-color: var(--ac-primary-ink);
} }
.upload-input { .upload-input {
@ -371,22 +373,24 @@ onMounted(() => {
gap: 6px; gap: 6px;
font-size: 13px; font-size: 13px;
cursor: pointer; cursor: pointer;
color: var(--ac-text);
} }
.btn-cancel { .btn-cancel {
display: block; display: block;
text-align: center; text-align: center;
padding: 8px; padding: 8px;
border: 1px solid var(--ac-input-border);
border: 1px solid var(--ac-danger-bg);
border-radius: var(--song-radius-l); border-radius: var(--song-radius-l);
color: var(--ac-text-secondary);
color: var(--ac-danger-ink);
background: var(--ac-danger-bg);
text-decoration: none; text-decoration: none;
font-size: 13px; font-size: 13px;
transition: border-color var(--song-dur-1) var(--song-ease), color var(--song-dur-1) var(--song-ease);
transition: background var(--song-dur-1) var(--song-ease), color var(--song-dur-1) var(--song-ease), border-color var(--song-dur-1) var(--song-ease);
} }
.btn-cancel:hover { .btn-cancel:hover {
border-color: var(--ac-primary);
color: var(--ac-primary-ink);
border-color: var(--ac-danger-ink);
color: var(--ac-danger-ink);
} }
</style> </style>
Loading…
Cancel
Save