refactor: 更新UI 样式
This commit is contained in:
parent
f4cb991ed7
commit
41a4d997fd
|
|
@ -30,7 +30,7 @@
|
|||
:project-name="currentProject.name"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="explorer-body explorer-empty">
|
||||
<div v-else class="explorer-body empty-state">
|
||||
<span v-html="icons.folderLg" style="color: var(--text-tertiary); opacity: 0.5;" />
|
||||
<p>当前对话未关联项目</p>
|
||||
</div>
|
||||
|
|
@ -653,17 +653,7 @@ onMounted(() => {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.explorer-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* modal-overlay, modal-content, btn-icon, form-group, modal-footer, btn-secondary, btn-primary now in global.css */
|
||||
/* explorer-empty now uses global .empty-state */
|
||||
|
||||
.create-modal {
|
||||
background: var(--bg-primary);
|
||||
|
|
|
|||
|
|
@ -15,15 +15,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loadingTree" class="explorer-loading">
|
||||
<div v-if="loadingTree" class="empty-state">
|
||||
<span class="spinner" v-html="icons.spinnerMd" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="treeItems.length === 0" class="explorer-empty">
|
||||
<div v-else-if="treeItems.length === 0" class="empty-state">
|
||||
空项目
|
||||
</div>
|
||||
|
||||
<div v-else class="tree-container">
|
||||
<div v-else class="scrollbar-thin" style="flex: 1; padding: 4px 0;">
|
||||
<FileTreeItem
|
||||
v-for="item in treeItems"
|
||||
:key="item.path"
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Empty state -->
|
||||
<div v-else class="viewer-placeholder">
|
||||
<div v-else class="empty-state">
|
||||
<span v-html="icons.fileLg" style="color: var(--text-tertiary); opacity: 0.5;" />
|
||||
<span>选择文件以预览</span>
|
||||
</div>
|
||||
|
|
@ -311,31 +311,6 @@ onUnmounted(() => {
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tree-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.tree-container::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.tree-container::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar-thumb);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.explorer-loading,
|
||||
.explorer-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* -- Viewer -- */
|
||||
.file-viewer {
|
||||
flex: 1;
|
||||
|
|
@ -400,17 +375,6 @@ onUnmounted(() => {
|
|||
font-size: 13px;
|
||||
}
|
||||
|
||||
.viewer-placeholder {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* -- Code pane -- */
|
||||
.code-pane {
|
||||
flex: 1;
|
||||
|
|
|
|||
|
|
@ -30,13 +30,13 @@
|
|||
<div class="message-footer">
|
||||
<span class="token-count" v-if="tokenCount">{{ tokenCount }} tokens</span>
|
||||
<span class="message-time">{{ formatTime(createdAt) }}</span>
|
||||
<button v-if="role === 'assistant'" class="btn-regenerate" @click="$emit('regenerate')" title="重新生成">
|
||||
<button v-if="role === 'assistant'" class="ghost-btn success" @click="$emit('regenerate')" title="重新生成">
|
||||
<span v-html="icons.regenerate" />
|
||||
</button>
|
||||
<button v-if="role === 'assistant'" class="btn-copy" @click="copyContent" title="复制">
|
||||
<button v-if="role === 'assistant'" class="ghost-btn accent" @click="copyContent" title="复制">
|
||||
<span v-html="icons.copy" />
|
||||
</button>
|
||||
<button v-if="deletable" class="btn-delete-msg" @click="$emit('delete')" title="删除">
|
||||
<button v-if="deletable" class="ghost-btn danger" @click="$emit('delete')" title="删除">
|
||||
<span v-html="icons.trash" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -140,32 +140,6 @@ function copyContent() {
|
|||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.btn-regenerate,
|
||||
.btn-copy,
|
||||
.btn-delete-msg {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-tertiary);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.15s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-regenerate:hover {
|
||||
color: var(--success-color);
|
||||
background: var(--success-bg);
|
||||
}
|
||||
|
||||
.btn-copy:hover {
|
||||
color: var(--accent-primary);
|
||||
background: var(--accent-primary-light);
|
||||
}
|
||||
|
||||
.btn-delete-msg:hover {
|
||||
color: var(--danger-color);
|
||||
background: var(--danger-bg);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<div v-for="(file, index) in uploadedFiles" :key="index" class="file-item">
|
||||
<span class="file-icon">{{ getFileIcon(file.extension) }}</span>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<button class="btn-remove-file" @click="removeFile(index)" title="移除">
|
||||
<button class="ghost-btn danger btn-remove-file" @click="removeFile(index)" title="移除">
|
||||
<span v-html="icons.close" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -213,25 +213,11 @@ defineExpose({ focus })
|
|||
}
|
||||
|
||||
.btn-remove-file {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-tertiary);
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: all 0.15s;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.btn-remove-file:hover {
|
||||
background: var(--danger-bg);
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<Teleport to="body">
|
||||
<Transition name="modal-fade">
|
||||
<Transition name="fade">
|
||||
<div v-if="state.visible" class="modal-overlay" @click.self="onCancel" @keydown.escape="onCancel">
|
||||
<div class="modal-dialog" role="dialog" :aria-modal="true">
|
||||
<h3 class="modal-title">{{ state.title }}</h3>
|
||||
|
|
@ -128,13 +128,6 @@ watch(() => state.visible, (v) => {
|
|||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* Transitions */
|
||||
.modal-fade-enter-active,
|
||||
.modal-fade-leave-active {
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.modal-fade-enter-from,
|
||||
.modal-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="settings-panel">
|
||||
<div class="settings-header">
|
||||
<div class="settings-title">
|
||||
<div class="panel-header">
|
||||
<div class="panel-title">
|
||||
<span v-html="icons.settings" />
|
||||
<h4>会话设置</h4>
|
||||
</div>
|
||||
|
|
@ -215,37 +215,7 @@ onMounted(loadModels)
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
.settings-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.settings-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.settings-title svg {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.settings-title h4 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* tab styles now in global.css */
|
||||
/* panel-header, panel-title, header-actions now in global.css */
|
||||
|
||||
.settings-body {
|
||||
flex: 1;
|
||||
|
|
|
|||
|
|
@ -15,21 +15,21 @@
|
|||
<span class="project-name">{{ group.name }}</span>
|
||||
<span class="conv-count">{{ group.conversations.length }}</span>
|
||||
<button
|
||||
class="btn-group-action"
|
||||
class="ghost-btn"
|
||||
title="新建对话"
|
||||
@click.stop="$emit('createInProject', { id: group.id, name: group.name })"
|
||||
>
|
||||
<span v-html="icons.plus" />
|
||||
</button>
|
||||
<button
|
||||
class="btn-group-action"
|
||||
class="ghost-btn"
|
||||
title="浏览文件"
|
||||
@click.stop="$emit('browseProject', { id: group.id, name: group.name })"
|
||||
>
|
||||
<span v-html="icons.folder" />
|
||||
</button>
|
||||
<button
|
||||
class="btn-group-action btn-delete-project"
|
||||
class="ghost-btn danger btn-delete-project"
|
||||
title="删除项目"
|
||||
@click.stop="$emit('deleteProject', { id: group.id, name: group.name })"
|
||||
>
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
<span>{{ conv.message_count || 0 }} 条消息 · {{ formatTime(conv.updated_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-delete" @click.stop="$emit('delete', conv.id)" title="删除">
|
||||
<button class="ghost-btn danger btn-delete" @click.stop="$emit('delete', conv.id)" title="删除">
|
||||
<span v-html="icons.trash" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
<span class="standalone-icon" v-html="icons.chat" />
|
||||
<span class="conv-count">{{ groupedData.standalone.length }}</span>
|
||||
<button
|
||||
class="btn-group-action"
|
||||
class="ghost-btn"
|
||||
title="新建对话"
|
||||
@click.stop="$emit('createInProject', { id: null, name: null })"
|
||||
>
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
<span>{{ conv.message_count || 0 }} 条消息 · {{ formatTime(conv.updated_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-delete" @click.stop="$emit('delete', conv.id)" title="删除">
|
||||
<button class="ghost-btn danger btn-delete" @click.stop="$emit('delete', conv.id)" title="删除">
|
||||
<span v-html="icons.trash" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -229,10 +229,6 @@ function onScroll(e) {
|
|||
padding: 0 16px 16px;
|
||||
}
|
||||
|
||||
.conversation-list::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.conversation-list::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar-thumb-sidebar);
|
||||
border-radius: 2px;
|
||||
|
|
@ -297,16 +293,8 @@ function onScroll(e) {
|
|||
}
|
||||
|
||||
.btn-group-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-tertiary);
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.15s;
|
||||
opacity: 0.5;
|
||||
|
|
@ -316,15 +304,10 @@ function onScroll(e) {
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.btn-group-action:hover {
|
||||
color: var(--accent-primary);
|
||||
background: var(--accent-primary-light);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.btn-delete-project:hover {
|
||||
color: var(--danger-color);
|
||||
background: var(--danger-bg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
|
|
@ -369,24 +352,12 @@ function onScroll(e) {
|
|||
|
||||
.btn-delete {
|
||||
opacity: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-tertiary);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.15s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.conversation-item:hover .btn-delete {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.btn-delete:hover {
|
||||
color: var(--danger-color);
|
||||
background: var(--danger-bg);
|
||||
}
|
||||
|
||||
.loading-more,
|
||||
.empty-hint {
|
||||
|
|
@ -407,16 +378,13 @@ function onScroll(e) {
|
|||
}
|
||||
|
||||
.btn-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-tertiary);
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="stats-panel">
|
||||
<div class="stats-header">
|
||||
<div class="stats-title">
|
||||
<div class="panel-header">
|
||||
<div class="panel-title">
|
||||
<span v-html="icons.stats" />
|
||||
<h4>使用统计</h4>
|
||||
</div>
|
||||
|
|
@ -324,37 +324,7 @@ onMounted(loadStats)
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.stats-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stats-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.stats-title svg {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.stats-title h4 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* tab styles now in global.css */
|
||||
/* panel-header, panel-title, header-actions now in global.css */
|
||||
|
||||
.stats-loading {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -751,5 +751,101 @@ input[type="range"]::-moz-range-thumb {
|
|||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
/* ============ Ghost Button (base for small icon buttons) ============ */
|
||||
.ghost-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-tertiary);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.ghost-btn:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.ghost-btn.danger:hover {
|
||||
background: var(--danger-bg);
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.ghost-btn.accent:hover {
|
||||
background: var(--accent-primary-light);
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.ghost-btn.success:hover {
|
||||
background: var(--success-bg);
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
/* ============ Empty / Loading State ============ */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
gap: 12px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* ============ Thin Scrollbar (4px) ============ */
|
||||
.scrollbar-thin {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar-thumb);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* ============ Panel Header (title + actions) ============ */
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.panel-title svg {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.panel-title h4 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue