From bed2b2b08cd8911f0a146c5f18ff3f958a41068b Mon Sep 17 00:00:00 2001 From: engigu Date: Sat, 6 Dec 2025 20:13:09 +0800 Subject: [PATCH] chore: opt template code --- .../pages/messageTemplate/MessageTemplate.vue | 123 +--------------- .../pages/messageTemplate/TemplateEditor.vue | 138 +++++++++++++++++- 2 files changed, 136 insertions(+), 125 deletions(-) diff --git a/web/src/components/pages/messageTemplate/MessageTemplate.vue b/web/src/components/pages/messageTemplate/MessageTemplate.vue index d15bf7d..10f7561 100644 --- a/web/src/components/pages/messageTemplate/MessageTemplate.vue +++ b/web/src/components/pages/messageTemplate/MessageTemplate.vue @@ -3,10 +3,7 @@ import { ref, computed, reactive, onMounted } from 'vue' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' -import { Label } from '@/components/ui/label' import { Badge } from '@/components/ui/badge' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import EmptyTableState from '@/components/ui/EmptyTableState.vue' import Pagination from '@/components/ui/Pagination.vue' @@ -34,12 +31,6 @@ interface MessageTemplate { modified_on: string } -interface Placeholder { - key: string - label: string - default: string -} - const router = useRouter() let state = reactive({ @@ -51,9 +42,6 @@ let state = reactive({ status: 'all' }) -const isPreviewOpen = ref(false) -const currentTemplate = ref(null) - // API代码查看器状态 const isApiViewerOpen = ref(false) const selectedTemplateForApi = ref(null) @@ -67,13 +55,6 @@ const isEditorOpen = ref(false) const isEditing = ref(false) const selectedTemplateForEdit = ref(null) -const previewData = reactive({ - text: '', - html: '', - markdown: '', - params: {} as Record -}) - const totalPages = computed(() => Math.ceil(state.total / state.pageSize)) const queryListData = async (page: number, size: number, text = '', status = '') => { @@ -143,41 +124,6 @@ const handleViewLogs = (template: MessageTemplate) => { router.push(`/sendlogs?taskid=${template.id}`) } -const openPreview = async (template: MessageTemplate) => { - currentTemplate.value = template - - // 解析占位符 - let placeholders: Placeholder[] = [] - try { - placeholders = JSON.parse(template.placeholders || '[]') - } catch {} - - // 初始化预览参数 - previewData.params = {} - placeholders.forEach(p => { - previewData.params[p.key] = p.default || '' - }) - - await refreshPreview() - isPreviewOpen.value = true -} - -const refreshPreview = async () => { - if (!currentTemplate.value) return - - try { - const rsp = await request.post('/templates/preview', { - id: currentTemplate.value.id, - params: previewData.params - }) - previewData.text = rsp.data.data.text || '' - previewData.html = rsp.data.data.html || '' - previewData.markdown = rsp.data.data.markdown || '' - } catch (error: any) { - toast.error(error.response?.data?.message || '预览失败') - } -} - onMounted(async () => { await queryListData(1, state.pageSize) }) @@ -273,13 +219,10 @@ onMounted(async () => { {{ item.created_on }} - - - + + - - @@ -303,68 +246,6 @@ onMounted(async () => { @saved="handleEditorSaved" /> - - - - - 模板预览 - {{ currentTemplate?.name }} - -
- -
- -
- - -
-
- - - - - Text - HTML - Markdown - - -
-
{{ previewData.text || '无Text模板' }}
-
-
- -
-
-
-
-
- 查看HTML源码 -
{{ previewData.html }}
-
-
-
- -
-
{{ previewData.markdown || '无Markdown模板' }}
-
-
-
-
- - - -
-
- { return placeholdersList.value.filter(ph => ph.key && ph.key.trim()) }) +// 预览数据 +const previewData = ref({ + text: '', + html: '', + markdown: '', + params: {} as Record +}) + +// 是否显示预览 +const showPreview = ref(false) + +// 预览防抖定时器 +let previewDebounceTimer: number | null = null + +// 刷新预览 +const refreshPreview = async () => { + if (!props.isEditing || !formData.value.id) { + // 新建模板时,直接使用当前输入的内容作为预览 + previewData.value.text = replacePreviewPlaceholders(formData.value.text_template) + previewData.value.html = replacePreviewPlaceholders(formData.value.html_template) + previewData.value.markdown = replacePreviewPlaceholders(formData.value.markdown_template) + return + } + + try { + const rsp = await request.post('/templates/preview', { + id: formData.value.id, + params: previewData.value.params + }) + previewData.value.text = rsp.data.data.text || '' + previewData.value.html = rsp.data.data.html || '' + previewData.value.markdown = rsp.data.data.markdown || '' + } catch (error: any) { + console.error('预览失败:', error) + } +} + +// 替换预览占位符(用于新建模板) +const replacePreviewPlaceholders = (template: string) => { + if (!template) return '' + let result = template + Object.keys(previewData.value.params).forEach(key => { + const value = previewData.value.params[key] || `{{${key}}}` + result = result.replace(new RegExp(`{{${key}}}`, 'g'), value) + }) + return result +} + +// 监听模板内容变化,自动刷新预览(防抖) +watch([ + () => formData.value.text_template, + () => formData.value.html_template, + () => formData.value.markdown_template, + () => previewData.value.params +], () => { + if (!showPreview.value) return + + if (previewDebounceTimer) { + clearTimeout(previewDebounceTimer) + } + previewDebounceTimer = window.setTimeout(() => { + refreshPreview() + }, 500) +}, { deep: true }) + // 监听占位符列表变化,同步到 formData(使用防抖) let placeholderDebounceTimer: number | null = null watch(placeholdersList, () => { @@ -164,6 +229,12 @@ const loadTemplateData = (template: TemplateData) => { } catch { placeholdersList.value = [] } + + // 初始化预览参数 + previewData.value.params = {} + placeholdersList.value.forEach(p => { + previewData.value.params[p.key] = p.default || '' + }) } // 保存模板 @@ -263,7 +334,7 @@ watch(() => props.open, (newVal) => {
- +
props.open, (newVal) => {
+
+
+ + +
+ + +
+ +
+
+ + +
+
+
+
+ Text @@ -317,8 +417,16 @@ watch(() => props.open, (newVal) => { ref="textTemplateRef" v-model="formData.text_template" placeholder="请输入纯文本模板内容,可使用 {{key}} 作为占位符" - rows="15" + :rows="showPreview ? 10 : 15" /> + + +
+ +
+
{{ previewData.text || '无内容' }}
+
+
@@ -343,8 +451,19 @@ watch(() => props.open, (newVal) => { ref="htmlTemplateRef" v-model="formData.html_template" placeholder="请输入HTML模板内容,可使用 {{key}} 作为占位符" - rows="15" + :rows="showPreview ? 10 : 15" /> + + +
+ +
+
+
+

+ 💡 HTML 预览仅显示基础结构,实际发送时可能包含邮件样式等 +

+
@@ -369,8 +488,19 @@ watch(() => props.open, (newVal) => { ref="markdownTemplateRef" v-model="formData.markdown_template" placeholder="请输入Markdown模板内容,可使用 {{key}} 作为占位符" - rows="15" + :rows="showPreview ? 10 : 15" /> + + +
+ +
+
{{ previewData.markdown || '无内容' }}
+
+

+ 💡 Markdown 在发送时会被渲染为对应格式(钉钉、企业微信等平台支持) +

+