feat(stacked-cards): 调整卡片组样式
This commit is contained in:
parent
9b75000841
commit
64c441ba36
|
|
@ -68,11 +68,11 @@
|
|||
<button v-if="isStreaming" class="action-btn stop" title="停止生成" @click="$emit('stop')">
|
||||
<StopCircle :size="20" />
|
||||
</button>
|
||||
<button v-else class="action-btn send" :class="{ active: canSend, loading: isProcessingAttachments }" :disabled="!canSend"
|
||||
:title="isProcessingAttachments ? '附件处理中...' : '发送消息 (Ctrl+Enter)'" @click="handleSend">
|
||||
<Loader2 v-if="isProcessingAttachments" :size="20" class="animate-spin" />
|
||||
<SendIcon v-else :size="20" />
|
||||
</button>
|
||||
<button v-else class="action-btn send" :class="{ active: canSend, loading: isProcessingAttachments }" :disabled="!canSend"
|
||||
:title="isProcessingAttachments ? '附件处理中...' : '发送消息 (Ctrl+Enter)'" @click="handleSend">
|
||||
<Loader2 v-if="isProcessingAttachments" :size="20" class="animate-spin" />
|
||||
<SendIcon v-else :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -97,11 +97,11 @@ import StackedCards from "@/components/ui/StackedCards.vue";
|
|||
import PlusIcon from "../icons/custom/PlusIcon.vue";
|
||||
import SendIcon from "../icons/custom/SendIcon.vue";
|
||||
|
||||
interface AttachmentWithProgress extends Attachment {
|
||||
uploading?: boolean;
|
||||
progress?: number;
|
||||
deleting?: boolean;
|
||||
}
|
||||
interface AttachmentWithProgress extends Attachment {
|
||||
uploading?: boolean;
|
||||
progress?: number;
|
||||
deleting?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
|
@ -182,19 +182,19 @@ function showThrottledToast(message: string, type: "error" = "error") {
|
|||
}
|
||||
|
||||
// 计算属性
|
||||
const charCount = computed(() => inputText.value.length);
|
||||
const isUploading = computed(() => attachments.value.some((a) => a.uploading));
|
||||
const isProcessingAttachments = computed(() =>
|
||||
attachments.value.some((a) => a.uploading || a.deleting),
|
||||
);
|
||||
const canSend = computed(() => {
|
||||
return (
|
||||
(inputText.value.trim().length > 0 || attachments.value.length > 0) &&
|
||||
!props.disabled &&
|
||||
charCount.value <= props.maxChars &&
|
||||
!isProcessingAttachments.value
|
||||
);
|
||||
});
|
||||
const charCount = computed(() => inputText.value.length);
|
||||
const isUploading = computed(() => attachments.value.some((a) => a.uploading));
|
||||
const isProcessingAttachments = computed(() =>
|
||||
attachments.value.some((a) => a.uploading || a.deleting),
|
||||
);
|
||||
const canSend = computed(() => {
|
||||
return (
|
||||
(inputText.value.trim().length > 0 || attachments.value.length > 0) &&
|
||||
!props.disabled &&
|
||||
charCount.value <= props.maxChars &&
|
||||
!isProcessingAttachments.value
|
||||
);
|
||||
});
|
||||
|
||||
// 自动调整高度
|
||||
function autoResize() {
|
||||
|
|
@ -382,28 +382,28 @@ async function uploadFileToServer(id: string, file: File) {
|
|||
}
|
||||
|
||||
// 移除附件
|
||||
async function removeAttachment(id: string | number) {
|
||||
const targetId = String(id);
|
||||
const index = attachments.value.findIndex((a) => a.id === targetId);
|
||||
if (index === -1) return;
|
||||
|
||||
const attachment = attachments.value[index];
|
||||
let deletedFromOss = false;
|
||||
|
||||
// 如果已上传到 OSS(不是本地 blob URL),则从 OSS 删除
|
||||
if (attachment.url && !attachment.url.startsWith("blob:")) {
|
||||
try {
|
||||
attachment.deleting = true;
|
||||
await nextTick();
|
||||
await chatApi.deleteAttachment(attachment.url);
|
||||
deletedFromOss = true;
|
||||
} catch (error) {
|
||||
console.error("删除 OSS 文件失败:", error);
|
||||
// 即使删除失败也继续移除本地引用
|
||||
} finally {
|
||||
attachment.deleting = false;
|
||||
}
|
||||
}
|
||||
async function removeAttachment(id: string | number) {
|
||||
const targetId = String(id);
|
||||
const index = attachments.value.findIndex((a) => a.id === targetId);
|
||||
if (index === -1) return;
|
||||
|
||||
const attachment = attachments.value[index];
|
||||
let deletedFromOss = false;
|
||||
|
||||
// 如果已上传到 OSS(不是本地 blob URL),则从 OSS 删除
|
||||
if (attachment.url && !attachment.url.startsWith("blob:")) {
|
||||
try {
|
||||
attachment.deleting = true;
|
||||
await nextTick();
|
||||
await chatApi.deleteAttachment(attachment.url);
|
||||
deletedFromOss = true;
|
||||
} catch (error) {
|
||||
console.error("删除 OSS 文件失败:", error);
|
||||
// 即使删除失败也继续移除本地引用
|
||||
} finally {
|
||||
attachment.deleting = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 释放 blob URL(如果是本地的)
|
||||
if (attachment.url.startsWith("blob:")) {
|
||||
|
|
@ -718,8 +718,8 @@ onMounted(() => {
|
|||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 0;
|
||||
width: 88px;
|
||||
height: 118px;
|
||||
width: 50px;
|
||||
height: 70px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -625,6 +625,13 @@ onBeforeUnmount(() => {
|
|||
}
|
||||
}
|
||||
|
||||
.group-list{
|
||||
gap:5px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
margin: auto 0;
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -175,7 +175,6 @@ function handleDelete() {
|
|||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
margin: 2px 8px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { NModal, NImage } from 'naive-ui'
|
||||
import PlusIcon from '../icons/custom/PlusIcon.vue'
|
||||
export interface CardItem {
|
||||
id: string | number
|
||||
title?: string
|
||||
|
|
@ -27,7 +28,7 @@ interface Props {
|
|||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
maxVisible: 5,
|
||||
spreadGap: 190,
|
||||
spreadGap: 120,
|
||||
supportsFiles: true,
|
||||
supportsVision: true,
|
||||
})
|
||||
|
|
@ -318,20 +319,20 @@ onUnmounted(() => {
|
|||
justify-content: center;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
/* 卡片大小 */
|
||||
.cards-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 90px;
|
||||
height: 130px;
|
||||
justify-content: flex-end;
|
||||
min-width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.card {
|
||||
position: absolute;
|
||||
width: 90px;
|
||||
height: 120px;
|
||||
width: 90%;
|
||||
height: 100%;
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--card-border-color, var(--ffffff, #FFF));
|
||||
background: url(<path-to-image>) lightgray 50% / cover no-repeat;
|
||||
|
|
|
|||
Loading…
Reference in New Issue