Files
ctms/frontend/src/components/ThreadComposer.vue
T
2026-01-12 11:14:04 +08:00

118 lines
3.3 KiB
Vue

<template>
<div class="thread-composer">
<div v-if="quoteItem" class="quote-box">
<div class="quote-meta">
{{ TEXT.common.actions.quote }} {{ displayUser(quoteItem.created_by, { users: userMap, members: memberMap }) }}
· {{ displayDateTime(quoteItem.created_at) }}
<el-button type="text" size="small" @click="$emit('clear-quote')">{{ TEXT.modules.knowledgeMedicalConsult.clearQuote }}</el-button>
</div>
<div class="quote-content">{{ quoteContent(quoteItem) }}</div>
</div>
<el-input v-model="contentProxy" type="textarea" rows="3" :placeholder="TEXT.common.placeholders.input" />
<div v-if="allowAttachments" class="upload-row">
<div class="upload-label">{{ TEXT.common.labels.attachments }}</div>
<el-upload v-model:file-list="fileListProxy" :auto-upload="false" multiple list-type="picture" class="thread-upload">
<el-button size="small" class="upload-button">{{ TEXT.common.actions.upload }}</el-button>
</el-upload>
</div>
<div class="actions">
<el-button v-if="showCancel" size="small" @click="$emit('cancel')">{{ TEXT.common.actions.cancel }}</el-button>
<el-button size="small" type="primary" :loading="submitting" @click="$emit('submit')">{{ TEXT.common.actions.submit }}</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue";
import type { UploadUserFile } from "element-plus";
import { displayDateTime, displayUser } from "../utils/display";
import { TEXT } from "../locales";
const props = defineProps<{
modelValue: string;
fileList: UploadUserFile[];
submitting?: boolean;
showCancel?: boolean;
allowAttachments?: boolean;
quoteItem?: any | null;
memberMap?: Record<string, string>;
userMap?: Record<string, string>;
}>();
const emit = defineEmits<{
(e: "update:modelValue", value: string): void;
(e: "update:fileList", value: UploadUserFile[]): void;
(e: "submit"): void;
(e: "cancel"): void;
(e: "clear-quote"): void;
}>();
const contentProxy = computed({
get: () => props.modelValue,
set: (val: string) => emit("update:modelValue", val),
});
const fileListProxy = computed({
get: () => props.fileList,
set: (val: UploadUserFile[]) => emit("update:fileList", val),
});
const quoteContent = (item: any) => (item?.is_deleted ? TEXT.modules.knowledgeMedicalConsult.quoteDeleted : item?.content || TEXT.common.fallback);
</script>
<style scoped>
.thread-composer {
margin-bottom: 8px;
}
.upload-row {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
}
.upload-label {
font-size: 12px;
color: #606266;
padding: 4px 8px;
border-radius: 999px;
background: #f2f4f7;
}
.thread-upload :deep(.el-upload-list) {
margin-top: 6px;
}
.upload-button {
border: 1px dashed #3b82f6;
color: #1d4ed8;
background: #eff6ff;
font-weight: 600;
}
.upload-button:hover {
border-color: #2563eb;
color: #1e40af;
background: #e0ecff;
}
.actions {
margin-top: 8px;
display: flex;
justify-content: flex-end;
gap: 8px;
}
.quote-box {
background: #f5f7fa;
border-left: 3px solid #dcdfe6;
padding: 8px 10px;
border-radius: 4px;
margin-bottom: 8px;
}
.quote-meta {
display: flex;
justify-content: space-between;
color: #909399;
font-size: 12px;
margin-bottom: 6px;
}
.quote-content {
white-space: pre-wrap;
}
</style>