feat(manuscript): 后台按正文格式标记渲染与保存
- 新增 utils/manuscript-content.ts:格式判定、bytemd 转 HTML、纯文本转段落 - 编辑弹窗打开时把 markdown / 纯文本转成富文本,否则 wangEditor 会把 # 和 ** 当成正文, 保存一次就把原稿改坏 - 保存统一写 contentFormat: 'html' - 列表加「正文格式」列,非 HTML 的行显式标出来
This commit is contained in:
@@ -79,6 +79,12 @@
|
||||
import { shallowRef, ref, reactive, watch, onBeforeUnmount } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject } from 'ele-admin-pro';
|
||||
import {
|
||||
detectFormat,
|
||||
markdownToHtml,
|
||||
normalizeFormat,
|
||||
plainTextToHtml
|
||||
} from '@/utils/manuscript-content';
|
||||
import { addManuscript, updateManuscript } from '@/api/cms/manuscript';
|
||||
import { Manuscript } from '@/api/cms/manuscript/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
@@ -147,6 +153,7 @@
|
||||
id: undefined,
|
||||
title: undefined,
|
||||
content: undefined,
|
||||
contentFormat: 'html',
|
||||
cover: undefined,
|
||||
deleted: undefined,
|
||||
tenantId: undefined,
|
||||
@@ -161,6 +168,27 @@
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 入库格式 → 编辑器 HTML。
|
||||
* 库里可能是 markdown 或纯文本(早期从 AI 结果粘进来的),wangEditor 只认 HTML,
|
||||
* 原样塞进去会在保存时被当成普通文字包进 <p>,等于把正文改坏。
|
||||
*/
|
||||
const toEditorHtml = (data: Manuscript): string => {
|
||||
const content = data.content || '';
|
||||
const format = normalizeFormat(data.contentFormat) || detectFormat(content);
|
||||
if (format === 'markdown') {
|
||||
message.info('这篇稿件存的是 Markdown,已转换为富文本,请确认排版后保存');
|
||||
return markdownToHtml(content);
|
||||
}
|
||||
if (format === 'text') {
|
||||
message.info(
|
||||
'这篇稿件存的是纯文本,已按段落转换为富文本,请确认排版后保存'
|
||||
);
|
||||
return plainTextToHtml(content);
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
const hasEditorContent = (html?: string) => {
|
||||
const text = String(html || '')
|
||||
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
||||
@@ -257,7 +285,8 @@
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form,
|
||||
content: contentHtml.value
|
||||
content: contentHtml.value,
|
||||
contentFormat: 'html'
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateManuscript : addManuscript;
|
||||
const msg = await saveOrUpdate(formData);
|
||||
@@ -294,7 +323,7 @@
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
assignObject(form, props.data);
|
||||
contentHtml.value = props.data.content || '';
|
||||
contentHtml.value = toEditorHtml(props.data);
|
||||
coverList.value = props.data.cover
|
||||
? [
|
||||
{
|
||||
|
||||
@@ -58,7 +58,11 @@
|
||||
</a-card>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<ManuscriptEdit v-model:visible="showEdit" :data="current" @done="reload" />
|
||||
<ManuscriptEdit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
<!-- 新增审核记录弹窗 -->
|
||||
<ReviewRecordModal
|
||||
v-model:visible="showReviewModal"
|
||||
@@ -71,7 +75,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { message, Modal, Tag } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import { toDateString } from 'ele-admin-pro';
|
||||
@@ -82,9 +86,14 @@
|
||||
import Search from './components/search.vue';
|
||||
import ManuscriptEdit from './components/manuscriptEdit.vue';
|
||||
import ReviewRecordModal from './components/reviewRecordModal.vue';
|
||||
import { pageManuscript, removeManuscript, removeBatchManuscript } from '@/api/cms/manuscript';
|
||||
import {
|
||||
pageManuscript,
|
||||
removeManuscript,
|
||||
removeBatchManuscript
|
||||
} from '@/api/cms/manuscript';
|
||||
import type { Manuscript, ManuscriptParam } from '@/api/cms/manuscript/model';
|
||||
import type { ReviewList } from '@/api/gxmu/reviewList/model';
|
||||
import { detectFormat, normalizeFormat } from '@/utils/manuscript-content';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
@@ -130,19 +139,36 @@
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
align: 'center',
|
||||
width: 90,
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '标题',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
align: 'center',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '封面',
|
||||
dataIndex: 'cover',
|
||||
key: 'cover',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '正文格式',
|
||||
dataIndex: 'contentFormat',
|
||||
key: 'contentFormat',
|
||||
align: 'center',
|
||||
width: 110,
|
||||
customRender: ({ record }: { record: Manuscript }) => {
|
||||
const format =
|
||||
normalizeFormat(record.contentFormat) || detectFormat(record.content);
|
||||
if (format === 'html') {
|
||||
return createVNode(Tag, { color: 'default' }, () => 'HTML');
|
||||
}
|
||||
return createVNode(Tag, { color: 'orange' }, () =>
|
||||
format === 'markdown' ? 'Markdown' : '纯文本'
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
@@ -157,7 +183,7 @@
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
align: 'center',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '当前审核状态',
|
||||
|
||||
Reference in New Issue
Block a user