Sfoglia il codice sorgente

style: 精简侧栏品牌与会话列表

samien 2 mesi fa
parent
commit
6c4a0fa6b4

+ 10 - 3
src/client/components/app/ResourceSidebar.vue

@@ -15,6 +15,7 @@ const props = withDefaults(defineProps<{
   searchPlaceholder?: string
   searchPlaceholder?: string
   emptyTitle?: string
   emptyTitle?: string
   emptyDescription?: string
   emptyDescription?: string
+  singleLine?: boolean
 }>(), {
 }>(), {
   activeId: '',
   activeId: '',
   loading: false,
   loading: false,
@@ -23,6 +24,7 @@ const props = withDefaults(defineProps<{
   searchPlaceholder: '搜索',
   searchPlaceholder: '搜索',
   emptyTitle: '暂无内容',
   emptyTitle: '暂无内容',
   emptyDescription: '新建一项开始使用。',
   emptyDescription: '新建一项开始使用。',
+  singleLine: false,
 })
 })
 
 
 const emit = defineEmits<{
 const emit = defineEmits<{
@@ -109,7 +111,7 @@ defineExpose({ focusSearch })
           <div v-if="row.section" class="sidebar-section-label" :class="{ 'sidebar-section-label--pinned': row.section === '已置顶' }" role="presentation"><span>{{ row.sectionLabel }}</span></div>
           <div v-if="row.section" class="sidebar-section-label" :class="{ 'sidebar-section-label--pinned': row.section === '已置顶' }" role="presentation"><span>{{ row.sectionLabel }}</span></div>
           <div
           <div
             class="sidebar-item"
             class="sidebar-item"
-            :class="{ active: row.item.id === activeId || row.item.active }"
+            :class="{ active: row.item.id === activeId || row.item.active, 'sidebar-item--single-line': singleLine }"
             role="option"
             role="option"
             tabindex="0"
             tabindex="0"
             :aria-selected="row.item.id === activeId || row.item.active"
             :aria-selected="row.item.id === activeId || row.item.active"
@@ -117,7 +119,7 @@ defineExpose({ focusSearch })
             @keydown.enter.prevent="emit('select', row.item.id)"
             @keydown.enter.prevent="emit('select', row.item.id)"
             @keydown.space.prevent="emit('select', row.item.id)"
             @keydown.space.prevent="emit('select', row.item.id)"
           >
           >
-            <span class="sidebar-item__icon" :class="{ 'sidebar-item__icon--avatar': !row.item.icon }">
+            <span v-if="!singleLine" class="sidebar-item__icon" :class="{ 'sidebar-item__icon--avatar': !row.item.icon }">
               <AppIcon v-if="row.item.icon" :name="row.item.icon" :size="15" />
               <AppIcon v-if="row.item.icon" :name="row.item.icon" :size="15" />
               <template v-else>{{ row.item.title.slice(0, 1).toUpperCase() }}</template>
               <template v-else>{{ row.item.title.slice(0, 1).toUpperCase() }}</template>
               <span v-if="row.item.status" class="presence" :class="`presence--${row.item.status}`" />
               <span v-if="row.item.status" class="presence" :class="`presence--${row.item.status}`" />
@@ -127,7 +129,7 @@ defineExpose({ focusSearch })
                 <strong><AppIcon v-if="row.item.pinned" name="pin" :size="10" />{{ row.item.title }}</strong>
                 <strong><AppIcon v-if="row.item.pinned" name="pin" :size="10" />{{ row.item.title }}</strong>
                 <small v-if="row.item.meta">{{ row.item.meta }}</small>
                 <small v-if="row.item.meta">{{ row.item.meta }}</small>
               </span>
               </span>
-              <span v-if="row.item.subtitle" class="sidebar-item__row sidebar-item__row--secondary">
+              <span v-if="!singleLine && row.item.subtitle" class="sidebar-item__row sidebar-item__row--secondary">
                 <span>{{ row.item.subtitle }}</span>
                 <span>{{ row.item.subtitle }}</span>
                 <b v-if="row.item.unread">{{ row.item.unread > 99 ? '99+' : row.item.unread }}</b>
                 <b v-if="row.item.unread">{{ row.item.unread > 99 ? '99+' : row.item.unread }}</b>
               </span>
               </span>
@@ -163,6 +165,11 @@ defineExpose({ focusSearch })
 .sidebar-item { position: relative; display: flex; align-items: center; gap: 7px; width: 100%; min-height: 42px; padding: 4px 7px; border: 0; border-radius: 8px; background: transparent; color: var(--text-primary); cursor: pointer; text-align: left; transition: background-color 120ms ease, color 120ms ease; }
 .sidebar-item { position: relative; display: flex; align-items: center; gap: 7px; width: 100%; min-height: 42px; padding: 4px 7px; border: 0; border-radius: 8px; background: transparent; color: var(--text-primary); cursor: pointer; text-align: left; transition: background-color 120ms ease, color 120ms ease; }
 .sidebar-item:hover { background: var(--surface-soft); }
 .sidebar-item:hover { background: var(--surface-soft); }
 .sidebar-item.active { background: var(--surface-hover); }
 .sidebar-item.active { background: var(--surface-hover); }
+.sidebar-item--single-line { min-height: 34px; padding-block: 3px; }
+.sidebar-item--single-line .sidebar-item__copy { display: block; }
+.sidebar-item--single-line .sidebar-item__row { min-height: 28px; }
+.sidebar-item--single-line .sidebar-item__row strong { font-size: 12px; }
+.sidebar-item--single-line .sidebar-item__more { top: 3px; }
 .sidebar-item__icon { position: relative; display: grid; place-items: center; width: 23px; height: 23px; flex: 0 0 23px; border: 0; border-radius: 7px; color: var(--text-muted); background: transparent; }
 .sidebar-item__icon { position: relative; display: grid; place-items: center; width: 23px; height: 23px; flex: 0 0 23px; border: 0; border-radius: 7px; color: var(--text-muted); background: transparent; }
 .sidebar-item.active .sidebar-item__icon, .sidebar-item:hover .sidebar-item__icon { color: var(--text-secondary); }
 .sidebar-item.active .sidebar-item__icon, .sidebar-item:hover .sidebar-item__icon { color: var(--text-secondary); }
 .sidebar-item__icon--avatar { background: var(--surface-hover); color: var(--text-secondary); font-size: 9px; font-weight: 700; }
 .sidebar-item__icon--avatar { background: var(--surface-hover); color: var(--text-secondary); font-size: 9px; font-weight: 700; }

+ 3 - 3
src/client/components/app/WorkspaceShell.vue

@@ -167,7 +167,7 @@ onBeforeUnmount(() => {
     >
     >
       <div class="sidebar-brand-row">
       <div class="sidebar-brand-row">
         <button class="rail__brand sidebar-brand" type="button" aria-label="返回对话" title="夭夭 Web" @click="navigate('/chat')">
         <button class="rail__brand sidebar-brand" type="button" aria-label="返回对话" title="夭夭 Web" @click="navigate('/chat')">
-          <BrandMark :size="sidebarCollapsed ? 26 : 32" :label="false" compact />
+          <BrandMark :size="sidebarCollapsed ? 26 : 32" :label="false" compact bare />
         </button>
         </button>
       </div>
       </div>
       <button
       <button
@@ -417,9 +417,9 @@ onBeforeUnmount(() => {
   color: var(--text-primary);
   color: var(--text-primary);
 }
 }
 
 
-.sidebar-brand-row { display: flex; min-height: 58px; align-items: center; padding: 9px 11px 7px; }
+.sidebar-brand-row { display: flex; min-height: 58px; align-items: center; padding: 9px 20px 7px; }
 .sidebar-brand { display: flex; min-width: 0; align-items: center; padding: 0; border: 0; border-radius: 10px; background: transparent; cursor: pointer; }
 .sidebar-brand { display: flex; min-width: 0; align-items: center; padding: 0; border: 0; border-radius: 10px; background: transparent; cursor: pointer; }
-.sidebar-brand:hover { background: var(--surface-hover); }
+.sidebar-brand:hover { background: transparent; }
 .sidebar-collapse, .sidebar-account__utility {
 .sidebar-collapse, .sidebar-account__utility {
   display: grid;
   display: grid;
   width: 34px;
   width: 34px;

+ 4 - 1
src/client/components/common/BrandMark.vue

@@ -3,15 +3,17 @@ withDefaults(defineProps<{
   size?: number
   size?: number
   label?: boolean
   label?: boolean
   compact?: boolean
   compact?: boolean
+  bare?: boolean
 }>(), {
 }>(), {
   size: 32,
   size: 32,
   label: false,
   label: false,
   compact: false,
   compact: false,
+  bare: false,
 })
 })
 </script>
 </script>
 
 
 <template>
 <template>
-  <span class="brand-mark" :class="{ 'brand-mark--compact': compact }">
+  <span class="brand-mark" :class="{ 'brand-mark--compact': compact, 'brand-mark--bare': bare }">
     <span class="brand-mark__plate" :style="{ width: `${size}px`, height: `${size}px` }">
     <span class="brand-mark__plate" :style="{ width: `${size}px`, height: `${size}px` }">
       <img src="/brand/AppIcon-1024.png" alt="" aria-hidden="true" />
       <img src="/brand/AppIcon-1024.png" alt="" aria-hidden="true" />
     </span>
     </span>
@@ -51,4 +53,5 @@ withDefaults(defineProps<{
 
 
 .brand-mark--compact { gap: 8px; }
 .brand-mark--compact { gap: 8px; }
 .brand-mark--compact .brand-mark__name { font-size: 16px; }
 .brand-mark--compact .brand-mark__name { font-size: 16px; }
+.brand-mark--bare .brand-mark__plate { border-radius: 0; background: transparent; }
 </style>
 </style>

+ 1 - 0
src/client/views/ChatView.vue

@@ -188,6 +188,7 @@ watch(() => chat.activeSessionId, async id => {
         :active-id="chat.activeSessionId"
         :active-id="chat.activeSessionId"
         :loading="chat.isLoading"
         :loading="chat.isLoading"
         :search="search"
         :search="search"
+        single-line
         search-placeholder="搜索会话"
         search-placeholder="搜索会话"
         empty-title="还没有会话"
         empty-title="还没有会话"
         empty-description="新建会话后,从输入框开始聊天。"
         empty-description="新建会话后,从输入框开始聊天。"