细节优化——1
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="item in items" :key="item.id" :timestamp="displayDateTime(item.created_at)">
|
||||
<div class="thread-item" :class="{ 'is-highlighted': isHighlighted(item) }">
|
||||
<div class="thread-meta">
|
||||
<strong>{{ displayUser(item.created_by, { users: userMap, members: memberMap }) }}</strong>
|
||||
<div class="thread-actions">
|
||||
<el-button v-if="canQuote" type="text" size="small" @click="$emit('quote', item)">引用</el-button>
|
||||
<el-button v-if="canDelete?.(item)" type="text" size="small" class="danger" @click="$emit('delete', item)">
|
||||
删除
|
||||
</el-button>
|
||||
<slot name="actions" :item="item" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.quote" class="quote-block">
|
||||
<div class="quote-meta">
|
||||
{{ displayUser(item.quote.created_by, { users: userMap, members: memberMap }) }}
|
||||
· {{ displayDateTime(item.quote.created_at) }}
|
||||
</div>
|
||||
<div class="quote-content">{{ quoteContent(item.quote) }}</div>
|
||||
</div>
|
||||
<div v-if="attachmentsMap[item.id]?.length" class="thread-attachments">
|
||||
<div v-for="file in attachmentsMap[item.id]" :key="file.id" class="attachment-item">
|
||||
<el-image
|
||||
v-if="isImage(file)"
|
||||
:src="attachmentUrl(file.id)"
|
||||
:preview-src-list="[attachmentUrl(file.id)]"
|
||||
fit="cover"
|
||||
preview-teleported
|
||||
class="attachment-image"
|
||||
/>
|
||||
<div v-if="isImage(file)" class="attachment-name" :title="file.filename">
|
||||
{{ file.filename }}
|
||||
</div>
|
||||
<el-link v-else :underline="false" class="file-link" @click="download(file.id)">
|
||||
<span class="file-badge">文件</span>
|
||||
<span class="file-name">{{ file.filename }}</span>
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{ contentText(item) }}</p>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { displayDateTime, displayUser } from "../utils/display";
|
||||
|
||||
const props = defineProps<{
|
||||
items: any[];
|
||||
attachmentsMap: Record<string, any[]>;
|
||||
memberMap?: Record<string, string>;
|
||||
userMap?: Record<string, string>;
|
||||
canQuote?: boolean;
|
||||
canDelete?: (item: any) => boolean;
|
||||
highlightIds?: Array<string | number>;
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
(e: "quote", item: any): void;
|
||||
(e: "delete", item: any): void;
|
||||
}>();
|
||||
|
||||
const quoteContent = (item: any) => (item?.is_deleted ? "内容已删除" : item?.content || "—");
|
||||
const contentText = (item: any) => (item?.is_deleted ? "内容已删除" : item?.content || "—");
|
||||
|
||||
const attachmentUrl = (id: string) => {
|
||||
const token = localStorage.getItem("ctms_token");
|
||||
return token ? `/api/v1/attachments/${id}/download?token=${token}` : `/api/v1/attachments/${id}/download`;
|
||||
};
|
||||
|
||||
const download = (id: string) => {
|
||||
window.open(attachmentUrl(id), "_blank");
|
||||
};
|
||||
|
||||
const isImage = (file: any) => {
|
||||
const type = String(file?.content_type || "").toLowerCase();
|
||||
if (type.startsWith("image/")) return true;
|
||||
const name = String(file?.filename || "").toLowerCase();
|
||||
return [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp"].some((ext) => name.endsWith(ext));
|
||||
};
|
||||
|
||||
const isHighlighted = (item: any) => {
|
||||
if (!props.highlightIds?.length) return false;
|
||||
return props.highlightIds.includes(item?.id);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.thread-item.is-highlighted {
|
||||
background: #fff7ed;
|
||||
border: 1px solid #fed7aa;
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.thread-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #edf1f7;
|
||||
background: #ffffff;
|
||||
margin: 6px 0;
|
||||
}
|
||||
.thread-item.is-highlighted {
|
||||
border-color: #fed7aa;
|
||||
}
|
||||
.thread-item p {
|
||||
margin: 4px 0 0;
|
||||
}
|
||||
.thread-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.thread-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.quote-block {
|
||||
background: #f5f7fa;
|
||||
border-left: 3px solid #dcdfe6;
|
||||
padding: 8px 10px;
|
||||
border-radius: 4px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.quote-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.quote-content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.thread-attachments {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.attachment-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
max-width: 140px;
|
||||
}
|
||||
.attachment-image {
|
||||
width: 128px;
|
||||
height: 96px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
background: #fafafa;
|
||||
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
.attachment-name {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
max-width: 128px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.file-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
}
|
||||
.file-link:hover {
|
||||
border-color: #cbd5f5;
|
||||
background: #eef2ff;
|
||||
}
|
||||
.file-badge {
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
background: #e2e8f0;
|
||||
padding: 2px 6px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
.file-name {
|
||||
max-width: 180px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.danger {
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user