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')">
|
<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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue