更新若干功能

This commit is contained in:
hero920103 committed 2026-10-21 17:45:50 +08:00
1 parent 892ba5886d
commit 79d5ae6b66
45 files changed
+6755 -38

No files matched your search

@@ -0,0 +1,498 @@
<template>
<el-drawer
v-model="visible"
title="日报详情"
size="600px"
@closed="handleClosed"
>
<div v-loading="loading" class="report-detail">
<template v-if="detail">
<div class="detail-head">
<el-avatar :size="40" class="author-avatar">
{{ (detail.report.user_name || '?').slice(0, 1) }}
</el-avatar>
<div class="head-main">
<div class="author-line">
<span class="author-name">{{ detail.report.user_name }}</span>
<el-tag
:type="detail.report.status === 1 ? 'success' : 'info'"
size="small"
>
{{ detail.report.status === 1 ? '已提交' : '草稿' }}
</el-tag>
</div>
<div class="author-sub">
{{ detail.report.org_name || '未分配部门' }} ·
{{ detail.report.report_date }}
<template v-if="detail.report.submit_time">
· 提交于 {{ detail.report.submit_time }}
</template>
</div>
</div>
<div class="head-actions">
<el-button
v-if="detail.can_edit"
link
type="primary"
@click="$emit('edit', detail.report)"
>
编辑
</el-button>
<el-button
v-if="detail.can_delete"
link
type="danger"
@click="handleDeleteReport"
>
删除
</el-button>
</div>
</div>
<section class="detail-section">
<h4>今日完成</h4>
<div class="section-text">{{ detail.report.today_done || '—' }}</div>
</section>
<section class="detail-section">
<h4>明日计划</h4>
<div class="section-text">{{ detail.report.tomorrow_plan || '—' }}</div>
</section>
<section class="detail-section">
<h4>问题与建议</h4>
<div class="section-text">{{ detail.report.issues || '—' }}</div>
</section>
<section v-if="detail.report.attachments?.length" class="detail-section">
<h4>附件</h4>
<div class="attachment-list">
<a
v-for="file in detail.report.attachments"
:key="file.url"
:href="file.url"
target="_blank"
rel="noopener"
class="attachment-item"
>
<el-icon><Paperclip /></el-icon>
<span class="file-name">{{ file.name || '附件' }}</span>
<span v-if="file.size" class="file-size">{{ formatSize(file.size) }}</span>
</a>
</div>
</section>
<section class="detail-section comment-section">
<h4>评论 <span class="comment-count">{{ detail.comments.length }}</span></h4>
<el-empty
v-if="!detail.comments.length"
description="还没有评论"
:image-size="60"
/>
<div v-else class="comment-list">
<div
v-for="item in detail.comments"
:key="item.id"
class="comment-item"
:class="{ 'is-reply': item.reply_to_id > 0 }"
>
<div class="comment-head">
<span class="comment-user">{{ item.user_name }}</span>
<span v-if="item.reply_to_name" class="reply-to">
回复 {{ item.reply_to_name }}
</span>
<span class="comment-time">{{ item.create_time }}</span>
</div>
<div class="comment-content">{{ item.content }}</div>
<div class="comment-actions">
<el-button link type="primary" @click="startReply(item)">
回复
</el-button>
<el-button
v-if="item.can_delete"
link
type="danger"
@click="handleDeleteComment(item)"
>
删除
</el-button>
</div>
</div>
</div>
<div v-if="detail.can_comment" class="comment-editor">
<div v-if="replyTo" class="reply-banner">
正在回复 {{ replyTo.user_name }}
<el-button link type="primary" @click="replyTo = null">取消</el-button>
</div>
<el-input
v-model="commentText"
type="textarea"
:rows="3"
maxlength="1000"
show-word-limit
:placeholder="replyTo ? `回复 ${replyTo.user_name}` : '写下你的评论或建议'"
/>
<div class="editor-actions">
<el-button
type="primary"
:loading="submitting"
@click="submitComment"
>
发表评论
</el-button>
</div>
</div>
<div v-else-if="detail.report.status !== 1" class="comment-disabled">
日报提交后才能评论
</div>
</section>
</template>
</div>
</el-drawer>
</template>
<script setup>
import { computed, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Paperclip } from '@element-plus/icons-vue'
import {
getDailyReportDetail,
addDailyReportComment,
deleteDailyReportComment,
deleteDailyReport
} from '@/api/oaDailyReport'
const props = defineProps({
modelValue: { type: Boolean, default: false },
reportId: { type: [Number, String], default: 0 }
})
const emit = defineEmits(['update:modelValue', 'edit', 'deleted'])
const visible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
const loading = ref(false)
const detail = ref(null)
const commentText = ref('')
const replyTo = ref(null)
const submitting = ref(false)
watch(
() => [props.modelValue, props.reportId],
([open]) => {
if (open && props.reportId) loadDetail()
},
{ immediate: true }
)
async function loadDetail() {
loading.value = true
try {
const res = await getDailyReportDetail(props.reportId)
detail.value = res?.data || null
if (!detail.value) ElMessage.error(res?.msg || '日报加载失败')
commentText.value = ''
replyTo.value = null
} catch (err) {
ElMessage.error(err?.message || '日报加载失败')
} finally {
loading.value = false
}
}
function startReply(comment) {
replyTo.value = { id: comment.id, user_name: comment.user_name }
}
async function submitComment() {
const content = commentText.value.trim()
if (!content) {
ElMessage.warning('请输入评论内容')
return
}
submitting.value = true
try {
await addDailyReportComment({
report_id: Number(props.reportId),
content,
reply_to_id: replyTo.value?.id || 0
})
ElMessage.success('评论成功')
commentText.value = ''
replyTo.value = null
await loadDetail()
} catch (err) {
ElMessage.error(err?.message || '评论失败')
} finally {
submitting.value = false
}
}
async function handleDeleteComment(comment) {
try {
await ElMessageBox.confirm('确定删除这条评论吗?', '删除评论', {
type: 'warning',
confirmButtonText: '删除',
cancelButtonText: '取消'
})
} catch {
return
}
try {
await deleteDailyReportComment(comment.id)
ElMessage.success('评论已删除')
await loadDetail()
} catch (err) {
ElMessage.error(err?.message || '删除失败')
}
}
async function handleDeleteReport() {
try {
await ElMessageBox.confirm(
'删除后不可恢复,且该日期的评论会一并删除。确定删除这条日报吗?',
'删除日报',
{ type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' }
)
} catch {
return
}
try {
await deleteDailyReport(props.reportId)
ElMessage.success('日报已删除')
visible.value = false
emit('deleted', Number(props.reportId))
} catch (err) {
ElMessage.error(err?.message || '删除失败')
}
}
function formatSize(size) {
const n = Number(size) || 0
if (n < 1024) return `${n} B`
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`
return `${(n / 1024 / 1024).toFixed(1)} MB`
}
function handleClosed() {
detail.value = null
commentText.value = ''
replyTo.value = null
}
</script>
<style lang="less" scoped>
.report-detail {
min-height: 200px;
}
.detail-head {
display: flex;
align-items: center;
gap: 12px;
padding-bottom: 14px;
border-bottom: 1px solid #ebeef5;
}
.author-avatar {
background: #409eff;
color: #fff;
font-size: 16px;
}
.head-main {
flex: 1;
min-width: 0;
}
.author-line {
display: flex;
align-items: center;
gap: 8px;
}
.author-name {
font-size: 15px;
font-weight: 600;
color: #303133;
}
.author-sub {
margin-top: 4px;
font-size: 12px;
color: #909399;
}
.head-actions {
display: flex;
align-items: center;
gap: 4px;
}
.detail-section {
padding: 14px 0;
border-bottom: 1px dashed #ebeef5;
h4 {
margin: 0 0 8px;
font-size: 14px;
color: #606266;
}
}
.section-text {
font-size: 14px;
line-height: 1.8;
color: #303133;
white-space: pre-wrap;
word-break: break-word;
}
.attachment-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.attachment-item {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border: 1px solid #ebeef5;
border-radius: 6px;
color: #409eff;
text-decoration: none;
background: #fafafa;
}
.file-name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-size {
font-size: 12px;
color: #909399;
}
.comment-count {
font-size: 12px;
color: #909399;
}
.comment-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.comment-item {
padding: 10px 12px;
border-radius: 6px;
background: #f7f8fa;
&.is-reply {
margin-left: 24px;
background: #f2f6fc;
}
}
.comment-head {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
}
.comment-user {
font-weight: 600;
color: #303133;
}
.reply-to {
color: #909399;
}
.comment-time {
margin-left: auto;
color: #c0c4cc;
}
.comment-content {
margin-top: 6px;
font-size: 13px;
line-height: 1.7;
color: #303133;
white-space: pre-wrap;
word-break: break-word;
}
.comment-actions {
margin-top: 4px;
}
.comment-editor {
margin-top: 14px;
}
.reply-banner {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
font-size: 12px;
color: #909399;
}
.editor-actions {
display: flex;
justify-content: flex-end;
margin-top: 8px;
}
.comment-disabled {
margin-top: 12px;
font-size: 12px;
color: #909399;
}
html.dark & {
.detail-head,
.detail-section {
border-color: var(--el-border-color-lighter);
}
.author-name,
.section-text,
.comment-user,
.comment-content {
color: var(--el-text-color-primary);
}
.author-sub,
.detail-section h4,
.comment-time,
.file-size,
.comment-disabled,
.reply-banner {
color: var(--el-text-color-secondary);
}
.attachment-item,
.comment-item {
background: var(--el-fill-color-light);
border-color: var(--el-border-color-lighter);
}
.attachment-item {
color: var(--el-color-primary);
}
}
</style>
@@ -0,0 +1,286 @@
<template>
<el-dialog
v-model="visible"
:title="isEdit ? '编辑日报' : '写日报'"
width="720px"
:close-on-click-modal="false"
@closed="handleClosed"
>
<el-form v-loading="loading" :model="form" label-width="88px">
<el-form-item label="日报日期" required>
<el-date-picker
v-model="form.date"
type="date"
value-format="YYYY-MM-DD"
:clearable="false"
disabled
style="width: 180px"
/>
<span class="date-tip">
可补写 {{ windowStart }} 至 {{ windowEnd }}
</span>
</el-form-item>
<el-form-item label="今日完成">
<el-input
v-model="form.today_done"
type="textarea"
:rows="5"
maxlength="5000"
show-word-limit
placeholder="今天完成了哪些工作?可按条列出"
/>
</el-form-item>
<el-form-item label="明日计划">
<el-input
v-model="form.tomorrow_plan"
type="textarea"
:rows="4"
maxlength="5000"
show-word-limit
placeholder="明天计划推进哪些事项?"
/>
</el-form-item>
<el-form-item label="问题与建议">
<el-input
v-model="form.issues"
type="textarea"
:rows="3"
maxlength="5000"
show-word-limit
placeholder="遇到的问题、需要的支持或改进建议(选填)"
/>
</el-form-item>
<el-form-item label="附件">
<div class="attachment-block">
<el-upload
:show-file-list="false"
:http-request="handleUpload"
accept=".jpg,.jpeg,.png,.gif,.webp,.bmp,.pdf"
>
<el-button :icon="Upload" :loading="uploading">上传附件</el-button>
</el-upload>
<span class="upload-tip">图片或 PDF,单个不超过 10MB</span>
<div v-if="form.attachments.length" class="attachment-list">
<div
v-for="(file, index) in form.attachments"
:key="file.url || index"
class="attachment-item"
>
<el-icon><Paperclip /></el-icon>
<a :href="file.url" target="_blank" rel="noopener" class="file-name">
{{ file.name || '附件' }}
</a>
<el-button link type="danger" @click="removeAttachment(index)">
移除
</el-button>
</div>
</div>
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button :loading="savingDraft" @click="handleSave(false)">保存草稿</el-button>
<el-button type="primary" :loading="savingSubmit" @click="handleSave(true)">
提交日报
</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { computed, reactive, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { Upload, Paperclip } from '@element-plus/icons-vue'
import { uploadFile } from '@/api/file'
import { saveDailyReport } from '@/api/oaDailyReport'
const props = defineProps({
modelValue: { type: Boolean, default: false },
// 传入已有日报时为编辑,否则为新建
report: { type: Object, default: null },
defaultDate: { type: String, default: '' },
windowStart: { type: String, default: '' },
windowEnd: { type: String, default: '' }
})
const emit = defineEmits(['update:modelValue', 'saved'])
const visible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
const isEdit = computed(() => Boolean(props.report?.id))
const loading = ref(false)
const uploading = ref(false)
const savingDraft = ref(false)
const savingSubmit = ref(false)
const form = reactive({
date: '',
today_done: '',
tomorrow_plan: '',
issues: '',
attachments: []
})
function fillForm(report) {
form.date = report?.report_date || props.defaultDate || todayStr()
form.today_done = report?.today_done || ''
form.tomorrow_plan = report?.tomorrow_plan || ''
form.issues = report?.issues || ''
form.attachments = Array.isArray(report?.attachments)
? report.attachments.map((item) => ({ ...item }))
: []
}
watch(
() => props.modelValue,
(open) => {
if (open) fillForm(props.report)
},
{ immediate: true }
)
function todayStr() {
const now = new Date()
const pad = (n) => String(n).padStart(2, '0')
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
}
async function handleUpload(options) {
const file = options.file
if (!file) return
const fd = new FormData()
fd.append('file', file)
uploading.value = true
try {
const res = await uploadFile(fd, { scope: 'user' })
const data = res?.data || {}
if (!data.url) throw new Error(res?.msg || '上传失败')
if (form.attachments.length >= 20) {
ElMessage.warning('附件最多 20 个')
return
}
form.attachments.push({
id: data.id || 0,
url: data.url,
name: data.name || file.name,
size: file.size || 0
})
} catch (err) {
ElMessage.error(err?.message || '上传失败')
} finally {
uploading.value = false
}
}
function removeAttachment(index) {
form.attachments.splice(index, 1)
}
async function handleSave(submit) {
if (!form.date) {
ElMessage.warning('请选择日报日期')
return
}
const hasContent =
form.today_done.trim() || form.tomorrow_plan.trim() || form.issues.trim()
if (!hasContent) {
ElMessage.warning('请至少填写今日完成、明日计划、问题与建议中的一项')
return
}
const setLoading = submit ? savingSubmit : savingDraft
setLoading.value = true
try {
const res = await saveDailyReport({
date: form.date,
today_done: form.today_done,
tomorrow_plan: form.tomorrow_plan,
issues: form.issues,
attachments: JSON.stringify(form.attachments),
submit
})
ElMessage.success(submit ? '日报已提交' : '草稿已保存')
emit('saved', res?.data || null)
visible.value = false
} catch (err) {
ElMessage.error(err?.message || '保存失败')
} finally {
setLoading.value = false
}
}
function handleClosed() {
loading.value = false
uploading.value = false
}
</script>
<style lang="less" scoped>
.date-tip {
margin-left: 10px;
font-size: 12px;
color: #909399;
}
.attachment-block {
width: 100%;
}
.upload-tip {
margin-left: 10px;
font-size: 12px;
color: #909399;
}
.attachment-list {
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
.attachment-item {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border: 1px solid #ebeef5;
border-radius: 6px;
background: #fafafa;
}
.file-name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #409eff;
text-decoration: none;
}
html.dark & {
.date-tip,
.upload-tip {
color: var(--el-text-color-secondary);
}
.attachment-item {
background: var(--el-fill-color-light);
border-color: var(--el-border-color-lighter);
}
.file-name {
color: var(--el-color-primary);
}
}
</style>
@@ -0,0 +1,487 @@
<template>
<div v-loading="loading" class="report-view">
<template v-if="detail">
<div class="detail-head">
<el-avatar :size="40" class="author-avatar">
{{ (detail.report.user_name || '?').slice(0, 1) }}
</el-avatar>
<div class="head-main">
<div class="author-line">
<span class="author-name">{{ detail.report.user_name }}</span>
<el-tag
:type="detail.report.status === 1 ? 'success' : 'info'"
size="small"
>
{{ detail.report.status === 1 ? '已提交' : '草稿' }}
</el-tag>
</div>
<div class="author-sub">
{{ detail.report.org_name || '未分配部门' }} ·
{{ detail.report.report_date }}
<template v-if="detail.report.submit_time">
· 提交于 {{ detail.report.submit_time }}
</template>
</div>
</div>
<div class="head-actions">
<el-button
v-if="detail.can_edit"
link
type="primary"
@click="$emit('edit', detail.report)"
>
编辑
</el-button>
<el-button
v-if="detail.can_delete"
link
type="danger"
@click="handleDeleteReport"
>
删除
</el-button>
</div>
</div>
<section class="detail-section">
<h4>今日完成</h4>
<div class="section-text">{{ detail.report.today_done || '—' }}</div>
</section>
<section class="detail-section">
<h4>明日计划</h4>
<div class="section-text">{{ detail.report.tomorrow_plan || '—' }}</div>
</section>
<section class="detail-section">
<h4>问题与建议</h4>
<div class="section-text">{{ detail.report.issues || '—' }}</div>
</section>
<section v-if="detail.report.attachments?.length" class="detail-section">
<h4>附件</h4>
<div class="attachment-list">
<a
v-for="file in detail.report.attachments"
:key="file.url"
:href="file.url"
target="_blank"
rel="noopener"
class="attachment-item"
>
<el-icon><Paperclip /></el-icon>
<span class="file-name">{{ file.name || '附件' }}</span>
<span v-if="file.size" class="file-size">{{ formatSize(file.size) }}</span>
</a>
</div>
</section>
<section class="detail-section comment-section">
<h4>评论 <span class="comment-count">{{ detail.comments.length }}</span></h4>
<el-empty
v-if="!detail.comments.length"
description="还没有评论"
:image-size="60"
/>
<div v-else class="comment-list">
<div
v-for="item in detail.comments"
:key="item.id"
class="comment-item"
:class="{ 'is-reply': item.reply_to_id > 0 }"
>
<div class="comment-head">
<span class="comment-user">{{ item.user_name }}</span>
<span v-if="item.reply_to_name" class="reply-to">
回复 {{ item.reply_to_name }}
</span>
<span class="comment-time">{{ item.create_time }}</span>
</div>
<div class="comment-content">{{ item.content }}</div>
<div class="comment-actions">
<el-button link type="primary" @click="startReply(item)">
回复
</el-button>
<el-button
v-if="item.can_delete"
link
type="danger"
@click="handleDeleteComment(item)"
>
删除
</el-button>
</div>
</div>
</div>
<div v-if="detail.can_comment" class="comment-editor">
<div v-if="replyTo" class="reply-banner">
正在回复 {{ replyTo.user_name }}
<el-button link type="primary" @click="replyTo = null">取消</el-button>
</div>
<el-input
v-model="commentText"
type="textarea"
:rows="3"
maxlength="1000"
show-word-limit
:placeholder="replyTo ? `回复 ${replyTo.user_name}` : '写下你的评论或建议'"
/>
<div class="editor-actions">
<el-button
type="primary"
:loading="submitting"
@click="submitComment"
>
发表评论
</el-button>
</div>
</div>
<div v-else-if="detail.report.status !== 1" class="comment-disabled">
日报提交后才能评论
</div>
</section>
</template>
<el-empty
v-else-if="!loading"
description="选择左侧日报查看详情"
:image-size="80"
/>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Paperclip } from '@element-plus/icons-vue'
import {
getDailyReportDetail,
addDailyReportComment,
deleteDailyReportComment,
deleteDailyReport
} from '@/api/oaDailyReport'
const props = defineProps({
reportId: { type: [Number, String], default: 0 }
})
const emit = defineEmits(['edit', 'deleted', 'changed'])
const loading = ref(false)
const detail = ref(null)
const commentText = ref('')
const replyTo = ref(null)
const submitting = ref(false)
watch(
() => props.reportId,
(id) => {
if (id) loadDetail()
else detail.value = null
},
{ immediate: true }
)
async function loadDetail() {
loading.value = true
try {
const res = await getDailyReportDetail(props.reportId)
detail.value = res?.data || null
if (!detail.value) ElMessage.error(res?.msg || '日报加载失败')
commentText.value = ''
replyTo.value = null
} catch (err) {
detail.value = null
ElMessage.error(err?.message || '日报加载失败')
} finally {
loading.value = false
}
}
function startReply(comment) {
replyTo.value = { id: comment.id, user_name: comment.user_name }
}
async function submitComment() {
const content = commentText.value.trim()
if (!content) {
ElMessage.warning('请输入评论内容')
return
}
submitting.value = true
try {
await addDailyReportComment({
report_id: Number(props.reportId),
content,
reply_to_id: replyTo.value?.id || 0
})
ElMessage.success('评论成功')
commentText.value = ''
replyTo.value = null
await loadDetail()
emit('changed')
} catch (err) {
ElMessage.error(err?.message || '评论失败')
} finally {
submitting.value = false
}
}
async function handleDeleteComment(comment) {
try {
await ElMessageBox.confirm('确定删除这条评论吗?', '删除评论', {
type: 'warning',
confirmButtonText: '删除',
cancelButtonText: '取消'
})
} catch {
return
}
try {
await deleteDailyReportComment(comment.id)
ElMessage.success('评论已删除')
await loadDetail()
emit('changed')
} catch (err) {
ElMessage.error(err?.message || '删除失败')
}
}
async function handleDeleteReport() {
try {
await ElMessageBox.confirm(
'删除后不可恢复,且该日期的评论会一并删除。确定删除这条日报吗?',
'删除日报',
{ type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' }
)
} catch {
return
}
try {
await deleteDailyReport(props.reportId)
ElMessage.success('日报已删除')
emit('deleted', Number(props.reportId))
} catch (err) {
ElMessage.error(err?.message || '删除失败')
}
}
function formatSize(size) {
const n = Number(size) || 0
if (n < 1024) return `${n} B`
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`
return `${(n / 1024 / 1024).toFixed(1)} MB`
}
</script>
<style lang="less" scoped>
.report-view {
min-height: 200px;
}
.detail-head {
display: flex;
align-items: center;
gap: 12px;
padding-bottom: 14px;
border-bottom: 1px solid #ebeef5;
}
.author-avatar {
background: #409eff;
color: #fff;
font-size: 16px;
}
.head-main {
flex: 1;
min-width: 0;
}
.author-line {
display: flex;
align-items: center;
gap: 8px;
}
.author-name {
font-size: 15px;
font-weight: 600;
color: #303133;
}
.author-sub {
margin-top: 4px;
font-size: 12px;
color: #909399;
}
.head-actions {
display: flex;
align-items: center;
gap: 4px;
}
.detail-section {
padding: 14px 0;
border-bottom: 1px dashed #ebeef5;
h4 {
margin: 0 0 8px;
font-size: 14px;
color: #606266;
}
}
.section-text {
font-size: 14px;
line-height: 1.8;
color: #303133;
white-space: pre-wrap;
word-break: break-word;
}
.attachment-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.attachment-item {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border: 1px solid #ebeef5;
border-radius: 6px;
color: #409eff;
text-decoration: none;
background: #fafafa;
}
.file-name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-size {
font-size: 12px;
color: #909399;
}
.comment-count {
font-size: 12px;
color: #909399;
}
.comment-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.comment-item {
padding: 10px 12px;
border-radius: 6px;
background: #f7f8fa;
&.is-reply {
margin-left: 24px;
background: #f2f6fc;
}
}
.comment-head {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
}
.comment-user {
font-weight: 600;
color: #303133;
}
.reply-to {
color: #909399;
}
.comment-time {
margin-left: auto;
color: #c0c4cc;
}
.comment-content {
margin-top: 6px;
font-size: 13px;
line-height: 1.7;
color: #303133;
white-space: pre-wrap;
word-break: break-word;
}
.comment-actions {
margin-top: 4px;
}
.comment-editor {
margin-top: 14px;
}
.reply-banner {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
font-size: 12px;
color: #909399;
}
.editor-actions {
display: flex;
justify-content: flex-end;
margin-top: 8px;
}
.comment-disabled {
margin-top: 12px;
font-size: 12px;
color: #909399;
}
html.dark & {
.detail-head,
.detail-section {
border-color: var(--el-border-color-lighter);
}
.author-name,
.section-text,
.comment-user,
.comment-content {
color: var(--el-text-color-primary);
}
.author-sub,
.detail-section h4,
.comment-time,
.file-size,
.comment-disabled,
.reply-banner {
color: var(--el-text-color-secondary);
}
.attachment-item,
.comment-item {
background: var(--el-fill-color-light);
border-color: var(--el-border-color-lighter);
}
.attachment-item {
color: var(--el-color-primary);
}
}
</style>