发信改为事务内每 300 人分批写入,避免全校群发一次插入过大;数据库异常现在返回明确的 503,不再只显示模糊 500。

校级、学院级管理员支持按学院、身份、行政班、教学班、姓名/学号/工号筛选,也可指定最多 500 名收件人。所有结果均由服务端重新校验权限。
接入 CKEditor 5,支持标题、列表、引用和链接;正文扩展为 MySQL longtext。
富文本在收件箱、详情弹窗和已发送记录中统一经 DOMPurify 净化后展示。
页面改用系统现有的冷白、靛蓝、青绿色令牌,新增“选择收件人 → 编辑内容”工作台和移动端适配。
This commit is contained in:
2026-07-27 12:53:10 +08:00 Unverified
parent 33ae991e86
commit 06d40a7bf1
13 changed files with 9180 additions and 221 deletions
+80
View File
@@ -0,0 +1,80 @@
<script setup lang="ts">
import { computed } from 'vue'
import DOMPurify from 'dompurify'
const props = defineProps<{
content: string
rich?: boolean
}>()
const safeHtml = computed(() => DOMPurify.sanitize(props.content, {
ALLOWED_TAGS: [
'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's',
'h2', 'h3', 'h4', 'ul', 'ol', 'li', 'blockquote', 'a',
],
ALLOWED_ATTR: ['href', 'target', 'rel'],
ALLOW_DATA_ATTR: false,
}))
</script>
<template>
<div v-if="rich" class="rich-message-content" v-html="safeHtml" />
<p v-else class="plain-message-content">{{ content }}</p>
</template>
<style scoped>
.plain-message-content {
margin: 0;
white-space: pre-wrap;
}
.rich-message-content {
overflow-wrap: anywhere;
color: inherit;
font-size: inherit;
line-height: 1.75;
}
.rich-message-content :deep(p),
.rich-message-content :deep(ul),
.rich-message-content :deep(ol),
.rich-message-content :deep(blockquote) {
margin: 0 0 .65em;
}
.rich-message-content :deep(p:last-child),
.rich-message-content :deep(ul:last-child),
.rich-message-content :deep(ol:last-child),
.rich-message-content :deep(blockquote:last-child) {
margin-bottom: 0;
}
.rich-message-content :deep(h2),
.rich-message-content :deep(h3),
.rich-message-content :deep(h4) {
margin: .8em 0 .35em;
color: var(--ink);
font-family: inherit;
}
.rich-message-content :deep(h2) { font-size: 1.25em; }
.rich-message-content :deep(h3) { font-size: 1.12em; }
.rich-message-content :deep(h4) { font-size: 1em; }
.rich-message-content :deep(ul),
.rich-message-content :deep(ol) {
padding-left: 1.5em;
}
.rich-message-content :deep(blockquote) {
padding: .55em .9em;
border-left: 3px solid var(--teal);
background: #f3f8f7;
}
.rich-message-content :deep(a) {
color: var(--indigo);
text-decoration: underline;
text-underline-offset: 2px;
}
</style>