feat(stacked-cards): 调整卡片组样式

This commit is contained in:
肖应宇 2026-03-31 15:26:50 +08:00
parent 9b75000841
commit 64c441ba36
4 changed files with 62 additions and 55 deletions

View File

@ -68,11 +68,11 @@
<button v-if="isStreaming" class="action-btn stop" title="停止生成" @click="$emit('stop')"> <button v-if="isStreaming" class="action-btn stop" title="停止生成" @click="$emit('stop')">
<StopCircle :size="20" /> <StopCircle :size="20" />
</button> </button>
<button v-else class="action-btn send" :class="{ active: canSend, loading: isProcessingAttachments }" :disabled="!canSend" <button v-else class="action-btn send" :class="{ active: canSend, loading: isProcessingAttachments }" :disabled="!canSend"
:title="isProcessingAttachments ? '附件处理中...' : '发送消息 (Ctrl+Enter)'" @click="handleSend"> :title="isProcessingAttachments ? '附件处理中...' : '发送消息 (Ctrl+Enter)'" @click="handleSend">
<Loader2 v-if="isProcessingAttachments" :size="20" class="animate-spin" /> <Loader2 v-if="isProcessingAttachments" :size="20" class="animate-spin" />
<SendIcon v-else :size="20" /> <SendIcon v-else :size="20" />
</button> </button>
</div> </div>
</div> </div>
</div> </div>
@ -97,11 +97,11 @@ import StackedCards from "@/components/ui/StackedCards.vue";
import PlusIcon from "../icons/custom/PlusIcon.vue"; import PlusIcon from "../icons/custom/PlusIcon.vue";
import SendIcon from "../icons/custom/SendIcon.vue"; import SendIcon from "../icons/custom/SendIcon.vue";
interface AttachmentWithProgress extends Attachment { interface AttachmentWithProgress extends Attachment {
uploading?: boolean; uploading?: boolean;
progress?: number; progress?: number;
deleting?: boolean; deleting?: boolean;
} }
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -182,19 +182,19 @@ function showThrottledToast(message: string, type: "error" = "error") {
} }
// //
const charCount = computed(() => inputText.value.length); const charCount = computed(() => inputText.value.length);
const isUploading = computed(() => attachments.value.some((a) => a.uploading)); const isUploading = computed(() => attachments.value.some((a) => a.uploading));
const isProcessingAttachments = computed(() => const isProcessingAttachments = computed(() =>
attachments.value.some((a) => a.uploading || a.deleting), attachments.value.some((a) => a.uploading || a.deleting),
); );
const canSend = computed(() => { const canSend = computed(() => {
return ( return (
(inputText.value.trim().length > 0 || attachments.value.length > 0) && (inputText.value.trim().length > 0 || attachments.value.length > 0) &&
!props.disabled && !props.disabled &&
charCount.value <= props.maxChars && charCount.value <= props.maxChars &&
!isProcessingAttachments.value !isProcessingAttachments.value
); );
}); });
// //
function autoResize() { function autoResize() {
@ -382,28 +382,28 @@ async function uploadFileToServer(id: string, file: File) {
} }
// //
async function removeAttachment(id: string | number) { async function removeAttachment(id: string | number) {
const targetId = String(id); const targetId = String(id);
const index = attachments.value.findIndex((a) => a.id === targetId); const index = attachments.value.findIndex((a) => a.id === targetId);
if (index === -1) return; if (index === -1) return;
const attachment = attachments.value[index]; const attachment = attachments.value[index];
let deletedFromOss = false; let deletedFromOss = false;
// OSS blob URL OSS // OSS blob URL OSS
if (attachment.url && !attachment.url.startsWith("blob:")) { if (attachment.url && !attachment.url.startsWith("blob:")) {
try { try {
attachment.deleting = true; attachment.deleting = true;
await nextTick(); await nextTick();
await chatApi.deleteAttachment(attachment.url); await chatApi.deleteAttachment(attachment.url);
deletedFromOss = true; deletedFromOss = true;
} catch (error) { } catch (error) {
console.error("删除 OSS 文件失败:", error); console.error("删除 OSS 文件失败:", error);
// 使 // 使
} finally { } finally {
attachment.deleting = false; attachment.deleting = false;
} }
} }
// blob URL // blob URL
if (attachment.url.startsWith("blob:")) { if (attachment.url.startsWith("blob:")) {
@ -718,8 +718,8 @@ onMounted(() => {
display: grid; display: grid;
place-items: center; place-items: center;
border-radius: 0; border-radius: 0;
width: 88px; width: 50px;
height: 118px; height: 70px;
background-color: rgb(255, 255, 255); background-color: rgb(255, 255, 255);
} }

View File

@ -625,6 +625,13 @@ onBeforeUnmount(() => {
} }
} }
.group-list{
gap:5px;
display:flex;
flex-direction:column;
padding: 0 20px;
}
.empty-state { .empty-state {
margin: auto 0; margin: auto 0;
display: flex; display: flex;

View File

@ -175,7 +175,6 @@ function handleDelete() {
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 10px 12px; padding: 10px 12px;
margin: 2px 8px;
border-radius: 10px; border-radius: 10px;
cursor: pointer; cursor: pointer;

View File

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue' import { ref, computed, onMounted, onUnmounted } from 'vue'
import { NModal, NImage } from 'naive-ui' import { NModal, NImage } from 'naive-ui'
import PlusIcon from '../icons/custom/PlusIcon.vue'
export interface CardItem { export interface CardItem {
id: string | number id: string | number
title?: string title?: string
@ -27,7 +28,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
maxVisible: 5, maxVisible: 5,
spreadGap: 190, spreadGap: 120,
supportsFiles: true, supportsFiles: true,
supportsVision: true, supportsVision: true,
}) })
@ -318,20 +319,20 @@ onUnmounted(() => {
justify-content: center; justify-content: center;
perspective: 1000px; perspective: 1000px;
} }
/* 卡片大小 */
.cards-wrapper { .cards-wrapper {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: flex-end;
min-width: 90px; min-width: 70px;
height: 130px; height: 70px;
} }
.card { .card {
position: absolute; position: absolute;
width: 90px; width: 90%;
height: 120px; height: 100%;
border-radius: 5px; border-radius: 5px;
border: 1px solid var(--card-border-color, var(--ffffff, #FFF)); border: 1px solid var(--card-border-color, var(--ffffff, #FFF));
background: url(<path-to-image>) lightgray 50% / cover no-repeat; background: url(<path-to-image>) lightgray 50% / cover no-repeat;