|
|
@ -1,7 +1,7 @@ |
|
|
|
<!-- 编辑弹窗 --> |
|
|
|
<template> |
|
|
|
<ele-modal |
|
|
|
width="80%" |
|
|
|
width="75%" |
|
|
|
:visible="visible" |
|
|
|
:maskClosable="false" |
|
|
|
:maxable="maxable" |
|
|
@ -29,12 +29,24 @@ |
|
|
|
<!-- </a-radio-group>--> |
|
|
|
<!-- </template>--> |
|
|
|
<a-tab-pane tab="基本信息" key="base"> |
|
|
|
<a-form-item label="文章标题" name="title"> |
|
|
|
<div class="title-input-container"> |
|
|
|
<a-input |
|
|
|
allow-clear |
|
|
|
style="width: 600px" |
|
|
|
placeholder="文章标题" |
|
|
|
v-model:value="form.title" |
|
|
|
@pressEnter="save" |
|
|
|
:maxlength="100" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</a-form-item> |
|
|
|
<a-form-item label="所属栏目" name="categoryId"> |
|
|
|
<a-tree-select |
|
|
|
allow-clear |
|
|
|
:tree-data="navigationList" |
|
|
|
tree-default-expand-all |
|
|
|
style="width: 558px" |
|
|
|
style="width: 320px" |
|
|
|
placeholder="请选择栏目" |
|
|
|
:value="form.categoryId || undefined" |
|
|
|
:listHeight="700" |
|
|
@ -43,24 +55,15 @@ |
|
|
|
@change="onCategoryId" |
|
|
|
/> |
|
|
|
</a-form-item> |
|
|
|
<a-form-item label="文章标题" name="title"> |
|
|
|
<a-input |
|
|
|
allow-clear |
|
|
|
style="width: 558px" |
|
|
|
placeholder="文章标题" |
|
|
|
v-model:value="form.title" |
|
|
|
@pressEnter="save" |
|
|
|
/> |
|
|
|
</a-form-item> |
|
|
|
<a-form-item label="封面图" name="image"> |
|
|
|
<SelectFile |
|
|
|
:placeholder="`请选择图片`" |
|
|
|
:limit="1" |
|
|
|
:data="images" |
|
|
|
@done="chooseImage" |
|
|
|
@del="onDeleteItem" |
|
|
|
/> |
|
|
|
</a-form-item> |
|
|
|
<!-- <a-form-item label="封面图" name="image">--> |
|
|
|
<!-- <SelectFile--> |
|
|
|
<!-- :placeholder="`请选择图片`"--> |
|
|
|
<!-- :limit="1"--> |
|
|
|
<!-- :data="images"--> |
|
|
|
<!-- @done="chooseImage"--> |
|
|
|
<!-- @del="onDeleteItem"--> |
|
|
|
<!-- />--> |
|
|
|
<!-- </a-form-item>--> |
|
|
|
<a-form-item label="预览图" name="files"> |
|
|
|
<SelectFile |
|
|
|
:placeholder="`请选择图片`" |
|
|
@ -70,6 +73,36 @@ |
|
|
|
@del="onDeleteFile" |
|
|
|
/> |
|
|
|
</a-form-item> |
|
|
|
<a-form-item label="内容详情"> |
|
|
|
<!-- 编辑器 --> |
|
|
|
<tinymce-editor |
|
|
|
ref="editorRef" |
|
|
|
v-if="editor == 1" |
|
|
|
class="editor-content" |
|
|
|
v-model:value="content" |
|
|
|
:disabled="disabled" |
|
|
|
:init="config" |
|
|
|
placeholder="支持直接粘贴或拖拽图片,也可点击工具栏图片按钮从文件库选择" |
|
|
|
/> |
|
|
|
<div class="file-selector-tip" v-if="editor == 1"> |
|
|
|
💡 提示:工具栏"图片"按钮从图片库选择,"上传"按钮快速上传图片;"视频"按钮从视频库选择,"上传视频"按钮快速上传视频;"一键排版"按钮自动优化文章格式;"首行缩进"按钮切换段落缩进 |
|
|
|
</div> |
|
|
|
<MdEditor |
|
|
|
v-if="editor == 2" |
|
|
|
v-model="content" |
|
|
|
:disabled="disabled" |
|
|
|
/> |
|
|
|
<a-space |
|
|
|
class="py-2 flex items-center text-gray-400" |
|
|
|
v-if="lang == 'zh_CN'" |
|
|
|
> |
|
|
|
<a-switch |
|
|
|
checked-children="AI翻译" |
|
|
|
v-model:checked="form.translation" |
|
|
|
/> |
|
|
|
<div v-if="form.translation">启用后,将自动翻译其他语言版本</div> |
|
|
|
</a-space> |
|
|
|
</a-form-item> |
|
|
|
<a-form-item label="摘要"> |
|
|
|
<a-textarea |
|
|
|
:rows="3" |
|
|
@ -100,36 +133,6 @@ |
|
|
|
</a-radio-group> |
|
|
|
</a-form-item> |
|
|
|
</a-tab-pane> |
|
|
|
<a-tab-pane tab="内容详情" key="content"> |
|
|
|
<!-- 编辑器 --> |
|
|
|
<tinymce-editor |
|
|
|
ref="editorRef" |
|
|
|
v-if="editor == 1" |
|
|
|
class="editor-content" |
|
|
|
v-model:value="content" |
|
|
|
:disabled="disabled" |
|
|
|
:init="config" |
|
|
|
placeholder="支持直接粘贴或拖拽图片,也可点击工具栏图片按钮从文件库选择" |
|
|
|
/> |
|
|
|
<div class="file-selector-tip" v-if="editor == 1"> |
|
|
|
💡 提示:工具栏"图片"按钮从图片库选择,"上传"按钮快速上传图片;"视频"按钮从视频库选择,"上传视频"按钮快速上传视频 |
|
|
|
</div> |
|
|
|
<MdEditor |
|
|
|
v-if="editor == 2" |
|
|
|
v-model="content" |
|
|
|
:disabled="disabled" |
|
|
|
/> |
|
|
|
<a-space |
|
|
|
class="py-2 flex items-center text-gray-400" |
|
|
|
v-if="lang == 'zh_CN'" |
|
|
|
> |
|
|
|
<a-switch |
|
|
|
checked-children="AI翻译" |
|
|
|
v-model:checked="form.translation" |
|
|
|
/> |
|
|
|
<div v-if="form.translation">启用后,将自动翻译其他语言版本</div> |
|
|
|
</a-space> |
|
|
|
</a-tab-pane> |
|
|
|
<a-tab-pane tab="其他选项" key="other"> |
|
|
|
<a-form-item label="关键词" name="tags"> |
|
|
|
<a-select |
|
|
@ -229,6 +232,8 @@ |
|
|
|
class="file-selector-modal" |
|
|
|
@done="onVideoSelected" |
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
<script lang="ts" setup> |
|
|
@ -339,7 +344,7 @@ |
|
|
|
files: '', |
|
|
|
lang: locale.value || undefined, |
|
|
|
// 排序 |
|
|
|
sortNumber: undefined, |
|
|
|
sortNumber: 100, |
|
|
|
// 备注 |
|
|
|
comments: undefined, |
|
|
|
// 状态 |
|
|
@ -401,6 +406,10 @@ |
|
|
|
// 选择栏目 |
|
|
|
const onCategoryId = (id: number) => { |
|
|
|
form.categoryId = id; |
|
|
|
// 💾 在新增模式下,用户手动选择栏目时也保存到本地存储 |
|
|
|
if (!isUpdate.value && id) { |
|
|
|
saveLastCategory(id); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const onChange = () => { |
|
|
@ -453,7 +462,7 @@ |
|
|
|
|
|
|
|
// 自定义工具栏,移除默认的image和media按钮,添加自定义按钮 |
|
|
|
toolbar: [ |
|
|
|
'fullscreen preview code codesample emoticons custom_image_selector quick_upload custom_video_selector quick_video_upload', |
|
|
|
'fullscreen preview code codesample emoticons custom_image_selector quick_upload custom_video_selector quick_video_upload auto_format toggle_indent', |
|
|
|
'undo redo | forecolor backcolor', |
|
|
|
'bold italic underline strikethrough', |
|
|
|
'alignleft aligncenter alignright alignjustify', |
|
|
@ -626,6 +635,27 @@ |
|
|
|
input.click(); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 添加一键排版按钮 |
|
|
|
editor.ui.registry.addButton('auto_format', { |
|
|
|
text: '一键排版', |
|
|
|
icon: 'template', |
|
|
|
tooltip: '智能优化文章格式和排版', |
|
|
|
onAction: () => { |
|
|
|
// 直接在这里处理排版,因为此时编辑器肯定已经初始化完成 |
|
|
|
handleAutoFormat(editor); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 添加段落首行缩进切换按钮 |
|
|
|
editor.ui.registry.addButton('toggle_indent', { |
|
|
|
text: '首行缩进', |
|
|
|
icon: 'indent', |
|
|
|
tooltip: '切换段落首行缩进(适合中文排版)', |
|
|
|
onAction: () => { |
|
|
|
toggleParagraphIndent(editor); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
@ -657,8 +687,257 @@ |
|
|
|
showVideoSelector.value = false; |
|
|
|
}; |
|
|
|
|
|
|
|
// 🎨 智能一键排版 - 人性化设计 |
|
|
|
const handleAutoFormat = (editor: any) => { |
|
|
|
try { |
|
|
|
// 1. 检查内容 |
|
|
|
const content = editor.getContent(); |
|
|
|
if (!content || content.trim() === '' || content === '<p><br></p>' || content === '<p></p>') { |
|
|
|
message.warning({ |
|
|
|
content: '📝 请先输入一些内容,然后再使用一键排版功能', |
|
|
|
duration: 3 |
|
|
|
}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 显示友好的加载提示 |
|
|
|
const loadingMsg = message.loading({ |
|
|
|
content: '✨ 正在为您的文章进行智能排版优化...', |
|
|
|
duration: 0 |
|
|
|
}); |
|
|
|
|
|
|
|
// 3. 延迟执行,让用户看到加载效果 |
|
|
|
setTimeout(() => { |
|
|
|
try { |
|
|
|
const optimizedContent = smartFormatContent(content); |
|
|
|
editor.setContent(optimizedContent); |
|
|
|
|
|
|
|
loadingMsg(); |
|
|
|
|
|
|
|
// 4. 显示成功提示 |
|
|
|
message.success({ |
|
|
|
content: '🎉 排版优化完成!您的文章现在看起来更专业了', |
|
|
|
duration: 4 |
|
|
|
}); |
|
|
|
|
|
|
|
// 5. 可选:显示优化统计 |
|
|
|
showOptimizationStats(content, optimizedContent); |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
loadingMsg(); |
|
|
|
console.error('排版优化失败:', error); |
|
|
|
message.error({ |
|
|
|
content: '😅 排版优化遇到了问题,请检查文章内容后重试', |
|
|
|
duration: 4 |
|
|
|
}); |
|
|
|
} |
|
|
|
}, 800); // 给用户一个良好的反馈体验 |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error('一键排版功能错误:', error); |
|
|
|
message.error({ |
|
|
|
content: '🔧 功能暂时不可用,请刷新页面后重试', |
|
|
|
duration: 4 |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 📊 显示优化统计信息 |
|
|
|
const showOptimizationStats = (originalContent: string, optimizedContent: string) => { |
|
|
|
const stats = analyzeOptimization(originalContent, optimizedContent); |
|
|
|
|
|
|
|
if (stats.optimizations.length > 0) { |
|
|
|
message.info({ |
|
|
|
content: `📈 本次优化: ${stats.optimizations.join('、')}`, |
|
|
|
duration: 6 |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 🔍 分析优化效果 |
|
|
|
const analyzeOptimization = (original: string, optimized: string) => { |
|
|
|
const optimizations: string[] = []; |
|
|
|
|
|
|
|
// 检查各种优化项目 |
|
|
|
if ((optimized.match(/<h[1-6][^>]*style/g) || []).length > (original.match(/<h[1-6][^>]*style/g) || []).length) { |
|
|
|
optimizations.push('标题样式'); |
|
|
|
} |
|
|
|
|
|
|
|
if ((optimized.match(/<p[^>]*style/g) || []).length > (original.match(/<p[^>]*style/g) || []).length) { |
|
|
|
optimizations.push('段落格式'); |
|
|
|
} |
|
|
|
|
|
|
|
if ((optimized.match(/<img[^>]*style/g) || []).length > (original.match(/<img[^>]*style/g) || []).length) { |
|
|
|
optimizations.push('图片布局'); |
|
|
|
} |
|
|
|
|
|
|
|
if ((optimized.match(/<ul[^>]*style|<ol[^>]*style/g) || []).length > (original.match(/<ul[^>]*style|<ol[^>]*style/g) || []).length) { |
|
|
|
optimizations.push('列表格式'); |
|
|
|
} |
|
|
|
|
|
|
|
return { optimizations }; |
|
|
|
}; |
|
|
|
|
|
|
|
// 🎨 智能排版核心函数 - 简单而强大 |
|
|
|
const smartFormatContent = (content: string): string => { |
|
|
|
let optimized = content; |
|
|
|
|
|
|
|
// 1. 🏷️ 标题优化 - 让标题更有层次感 |
|
|
|
optimized = optimized.replace(/<h1([^>]*)>/g, '<h1$1 style="font-size: 28px; font-weight: 700; margin: 24px 0 16px 0; line-height: 1.3; color: #1a1a1a; border-bottom: 2px solid #e8e8e8; padding-bottom: 10px;">'); |
|
|
|
optimized = optimized.replace(/<h2([^>]*)>/g, '<h2$1 style="font-size: 24px; font-weight: 600; margin: 20px 0 14px 0; line-height: 1.4; color: #2c2c2c;">'); |
|
|
|
optimized = optimized.replace(/<h3([^>]*)>/g, '<h3$1 style="font-size: 20px; font-weight: 600; margin: 18px 0 12px 0; line-height: 1.4; color: #3c3c3c;">'); |
|
|
|
optimized = optimized.replace(/<h4([^>]*)>/g, '<h4$1 style="font-size: 16px; font-weight: 600; margin: 14px 0 8px 0; line-height: 1.4; color: #4c4c4c;">'); |
|
|
|
optimized = optimized.replace(/<h5([^>]*)>/g, '<h5$1 style="font-size: 14px; font-weight: 600; margin: 12px 0 6px 0; line-height: 1.4; color: #5c5c5c;">'); |
|
|
|
optimized = optimized.replace(/<h6([^>]*)>/g, '<h6$1 style="font-size: 13px; font-weight: 600; margin: 10px 0 5px 0; line-height: 1.4; color: #6c6c6c;">'); |
|
|
|
|
|
|
|
// 2. 📝 段落优化 - 让阅读更舒适 |
|
|
|
optimized = optimized.replace(/<p([^>]*)>/g, (match, attrs) => { |
|
|
|
if (!attrs.includes('style=')) { |
|
|
|
return `<p${attrs} style="line-height: 1.8; margin: 16px 0; text-indent: 2em; color: #333;">`; |
|
|
|
} |
|
|
|
return match; |
|
|
|
}); |
|
|
|
|
|
|
|
// 3. 🖼️ 图片优化 - 让图片更美观 |
|
|
|
optimized = optimized.replace(/<img([^>]*?)>/g, (match, attrs) => { |
|
|
|
if (!attrs.includes('style=')) { |
|
|
|
const hasAlt = attrs.includes('alt='); |
|
|
|
return `<img${attrs} style="max-width: 100%; height: auto; margin: 20px auto; display: block; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.1);"${!hasAlt ? ' alt="图片"' : ''}>`; |
|
|
|
} |
|
|
|
return match; |
|
|
|
}); |
|
|
|
|
|
|
|
// 4. 📋 列表优化 - 让列表更清晰 |
|
|
|
optimized = optimized.replace(/<ul([^>]*)>/g, '<ul$1 style="margin: 16px 0; padding-left: 24px; line-height: 1.6;">'); |
|
|
|
optimized = optimized.replace(/<ol([^>]*)>/g, '<ol$1 style="margin: 16px 0; padding-left: 24px; line-height: 1.6;">'); |
|
|
|
optimized = optimized.replace(/<li([^>]*)>/g, '<li$1 style="margin: 8px 0; color: #333;">'); |
|
|
|
|
|
|
|
// 5. 💬 引用优化 - 让引用更突出 |
|
|
|
optimized = optimized.replace(/<blockquote([^>]*)>/g, '<blockquote$1 style="margin: 20px 0; padding: 16px 20px; border-left: 4px solid #1890ff; background: linear-gradient(90deg, #f6f8fa 0%, #ffffff 100%); font-style: italic; color: #555;">'); |
|
|
|
|
|
|
|
// 6. 💻 代码优化 - 让代码更专业 |
|
|
|
optimized = optimized.replace(/<code([^>]*)>/g, '<code$1 style="background-color: #f1f3f4; padding: 2px 6px; border-radius: 4px; font-family: \'Fira Code\', Consolas, Monaco, monospace; font-size: 0.9em; color: #d73a49;">'); |
|
|
|
optimized = optimized.replace(/<pre([^>]*)>/g, '<pre$1 style="margin: 20px 0; padding: 20px; background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; overflow-x: auto; font-family: \'Fira Code\', Consolas, Monaco, monospace; font-size: 14px; line-height: 1.5;">'); |
|
|
|
|
|
|
|
// 7. 📊 表格优化 - 让表格更美观 |
|
|
|
optimized = optimized.replace(/<table([^>]*)>/g, '<table$1 style="width: 100%; border-collapse: collapse; margin: 20px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.1); border-radius: 8px; overflow: hidden;">'); |
|
|
|
optimized = optimized.replace(/<th([^>]*)>/g, '<th$1 style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px; text-align: left; font-weight: 600;">'); |
|
|
|
optimized = optimized.replace(/<td([^>]*)>/g, '<td$1 style="padding: 12px; border-bottom: 1px solid #eee; color: #333;">'); |
|
|
|
|
|
|
|
// 8. 🔗 链接优化 - 让链接更友好 |
|
|
|
optimized = optimized.replace(/<a([^>]*)>/g, '<a$1 style="color: #1890ff; text-decoration: none; border-bottom: 1px solid transparent; transition: border-bottom 0.2s ease;" onmouseover="this.style.borderBottom=\'1px solid #1890ff\'" onmouseout="this.style.borderBottom=\'1px solid transparent\'">'); |
|
|
|
|
|
|
|
// 9. ➖ 分隔线优化 - 让分隔线更优雅 |
|
|
|
optimized = optimized.replace(/<hr([^>]*)>/g, '<hr$1 style="border: none; height: 2px; background: linear-gradient(90deg, transparent, #e8e8e8, transparent); margin: 30px 0;">'); |
|
|
|
|
|
|
|
// 10. 🧹 清理多余空白 |
|
|
|
optimized = optimized.replace(/\s+/g, ' '); // 清理多余空格 |
|
|
|
optimized = optimized.replace(/<p[^>]*>\s*<\/p>/g, ''); // 清理空段落 |
|
|
|
optimized = optimized.replace(/(<\/[^>]+>)\s+(<[^>]+>)/g, '$1$2'); // 清理标签间空白 |
|
|
|
|
|
|
|
return optimized; |
|
|
|
}; |
|
|
|
|
|
|
|
// 🔄 段落首行缩进切换功能 |
|
|
|
const toggleParagraphIndent = (editor: any) => { |
|
|
|
try { |
|
|
|
const content = editor.getContent(); |
|
|
|
|
|
|
|
if (!content || content.trim() === '' || content === '<p><br></p>' || content === '<p></p>') { |
|
|
|
message.warning({ |
|
|
|
content: '📝 请先输入一些段落内容,然后再切换首行缩进', |
|
|
|
duration: 3 |
|
|
|
}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查当前是否有首行缩进 |
|
|
|
const hasIndent = content.includes('text-indent: 2em') || content.includes('text-indent:2em'); |
|
|
|
|
|
|
|
let newContent: string; |
|
|
|
let actionText: string; |
|
|
|
|
|
|
|
if (hasIndent) { |
|
|
|
// 移除首行缩进 |
|
|
|
newContent = removeIndentFromParagraphs(content); |
|
|
|
actionText = '已移除段落首行缩进'; |
|
|
|
} else { |
|
|
|
// 添加首行缩进 |
|
|
|
newContent = addIndentToParagraphs(content); |
|
|
|
actionText = '已添加段落首行缩进'; |
|
|
|
} |
|
|
|
|
|
|
|
editor.setContent(newContent); |
|
|
|
|
|
|
|
message.success({ |
|
|
|
content: `📐 ${actionText}`, |
|
|
|
duration: 3 |
|
|
|
}); |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error('首行缩进切换失败:', error); |
|
|
|
message.error({ |
|
|
|
content: '🔧 首行缩进切换失败,请重试', |
|
|
|
duration: 3 |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 为段落添加首行缩进 |
|
|
|
const addIndentToParagraphs = (content: string): string => { |
|
|
|
return content.replace(/<p([^>]*)>/g, (match, attrs) => { |
|
|
|
// 如果已经有 style 属性 |
|
|
|
if (attrs.includes('style=')) { |
|
|
|
// 检查是否已经有 text-indent |
|
|
|
if (attrs.includes('text-indent')) { |
|
|
|
// 更新现有的 text-indent |
|
|
|
return match.replace(/text-indent:\s*[^;]+;?/g, 'text-indent: 2em;'); |
|
|
|
} else { |
|
|
|
// 在现有 style 中添加 text-indent |
|
|
|
return match.replace(/style="([^"]*)"/, 'style="$1 text-indent: 2em;"'); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 添加新的 style 属性 |
|
|
|
return `<p${attrs} style="text-indent: 2em;">`; |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
// 从段落移除首行缩进 |
|
|
|
const removeIndentFromParagraphs = (content: string): string => { |
|
|
|
return content.replace(/<p([^>]*)>/g, (match, attrs) => { |
|
|
|
if (attrs.includes('text-indent')) { |
|
|
|
// 移除 text-indent 属性 |
|
|
|
let newAttrs = attrs.replace(/text-indent:\s*[^;]+;?\s*/g, ''); |
|
|
|
|
|
|
|
// 如果 style 属性变空了,移除整个 style 属性 |
|
|
|
newAttrs = newAttrs.replace(/style="\s*"/g, ''); |
|
|
|
newAttrs = newAttrs.replace(/style=''\s*/g, ''); |
|
|
|
|
|
|
|
return `<p${newAttrs}>`; |
|
|
|
} |
|
|
|
return match; |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
const { resetFields } = useForm(form, rules); |
|
|
|
|
|
|
|
// �💾 保存和恢复栏目选择的功能 |
|
|
|
const LAST_CATEGORY_KEY = 'cms_article_last_category'; |
|
|
|
|
|
|
|
// 保存最后选择的栏目到本地存储 |
|
|
|
const saveLastCategory = (categoryId: number | undefined) => { |
|
|
|
if (categoryId) { |
|
|
|
localStorage.setItem(LAST_CATEGORY_KEY, categoryId.toString()); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 从本地存储获取最后选择的栏目 |
|
|
|
const getLastCategory = (): number | undefined => { |
|
|
|
const saved = localStorage.getItem(LAST_CATEGORY_KEY); |
|
|
|
return saved ? parseInt(saved) : undefined; |
|
|
|
}; |
|
|
|
|
|
|
|
/* 保存编辑 */ |
|
|
|
const save = () => { |
|
|
|
if (!formRef.value) { |
|
|
@ -689,6 +968,12 @@ |
|
|
|
.then((msg) => { |
|
|
|
loading.value = false; |
|
|
|
message.success(msg); |
|
|
|
|
|
|
|
// 💾 保存成功后,记住当前选择的栏目(仅在新增时) |
|
|
|
if (!isUpdate.value && form.categoryId) { |
|
|
|
saveLastCategory(form.categoryId); |
|
|
|
} |
|
|
|
|
|
|
|
updateVisible(false); |
|
|
|
emit('done'); |
|
|
|
}) |
|
|
@ -705,9 +990,6 @@ |
|
|
|
watch( |
|
|
|
() => props.visible, |
|
|
|
(visible) => { |
|
|
|
if (props.categoryId) { |
|
|
|
form.categoryId = props.categoryId; |
|
|
|
} |
|
|
|
if (localStorage.getItem('Editor')) { |
|
|
|
editor.value = Number(localStorage.getItem('Editor')); |
|
|
|
} |
|
|
@ -717,7 +999,9 @@ |
|
|
|
category.value = []; |
|
|
|
files.value = []; |
|
|
|
content.value = ''; |
|
|
|
|
|
|
|
if (props.data) { |
|
|
|
// 编辑模式:加载现有文章数据 |
|
|
|
loading.value = true; |
|
|
|
const data = props.data; |
|
|
|
// 文章详情 |
|
|
@ -754,19 +1038,22 @@ |
|
|
|
}); |
|
|
|
} |
|
|
|
loading.value = false; |
|
|
|
|
|
|
|
// assignObject(form, props.data); |
|
|
|
// 文章分类 |
|
|
|
// if(props.data.categoryParent){ |
|
|
|
// category.value.push(props.data.categoryParent); |
|
|
|
// } |
|
|
|
// if(props.data.categoryChildren){ |
|
|
|
// category.value.push(props.data.categoryChildren); |
|
|
|
// } |
|
|
|
|
|
|
|
isUpdate.value = true; |
|
|
|
} else { |
|
|
|
// 新增模式:恢复上次选择的栏目 |
|
|
|
isUpdate.value = false; |
|
|
|
|
|
|
|
// 🎯 优先级设置栏目: |
|
|
|
// 1. 如果传入了 categoryId(从栏目页面点击添加),使用传入的 |
|
|
|
// 2. 否则使用上次保存的栏目 |
|
|
|
if (props.categoryId) { |
|
|
|
form.categoryId = props.categoryId; |
|
|
|
} else { |
|
|
|
const lastCategory = getLastCategory(); |
|
|
|
if (lastCategory) { |
|
|
|
form.categoryId = lastCategory; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
resetFields(); |
|
|
@ -821,4 +1108,118 @@ |
|
|
|
z-index: 9999 !important; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 排版选项弹窗样式 |
|
|
|
:deep(.format-options-modal) { |
|
|
|
.ant-modal { |
|
|
|
z-index: 10000 !important; |
|
|
|
} |
|
|
|
|
|
|
|
.ant-modal-mask { |
|
|
|
z-index: 9999 !important; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@keyframes shimmer { |
|
|
|
0% { |
|
|
|
transform: translateX(-100%); |
|
|
|
} |
|
|
|
100% { |
|
|
|
transform: translateX(100%); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.format-presets { |
|
|
|
.format-preset-grid { |
|
|
|
display: grid; |
|
|
|
grid-template-columns: repeat(2, 1fr); |
|
|
|
gap: 16px; |
|
|
|
margin-bottom: 24px; |
|
|
|
} |
|
|
|
|
|
|
|
.format-preset-card { |
|
|
|
border: 2px solid #e8e8e8; |
|
|
|
border-radius: 8px; |
|
|
|
padding: 16px; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.3s ease; |
|
|
|
background: #ffffff; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
border-color: #1890ff; |
|
|
|
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15); |
|
|
|
transform: translateY(-2px); |
|
|
|
} |
|
|
|
|
|
|
|
.preset-header { |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: center; |
|
|
|
margin-bottom: 8px; |
|
|
|
|
|
|
|
h3 { |
|
|
|
margin: 0; |
|
|
|
font-size: 16px; |
|
|
|
font-weight: 600; |
|
|
|
color: #1a1a1a; |
|
|
|
} |
|
|
|
|
|
|
|
.preset-icon { |
|
|
|
font-size: 24px; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.preset-description { |
|
|
|
color: #666; |
|
|
|
font-size: 14px; |
|
|
|
margin-bottom: 12px; |
|
|
|
line-height: 1.5; |
|
|
|
} |
|
|
|
|
|
|
|
.preset-preview { |
|
|
|
background: #f8f9fa; |
|
|
|
border-radius: 4px; |
|
|
|
padding: 12px; |
|
|
|
|
|
|
|
.preview-title { |
|
|
|
font-weight: 600; |
|
|
|
font-size: 14px; |
|
|
|
color: #1a1a1a; |
|
|
|
margin-bottom: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
.preview-text { |
|
|
|
font-size: 12px; |
|
|
|
color: #666; |
|
|
|
line-height: 1.4; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.format-tips { |
|
|
|
background: #f6f8fa; |
|
|
|
border-radius: 8px; |
|
|
|
padding: 16px; |
|
|
|
border-left: 4px solid #1890ff; |
|
|
|
|
|
|
|
h4 { |
|
|
|
margin: 0 0 12px 0; |
|
|
|
font-size: 14px; |
|
|
|
font-weight: 600; |
|
|
|
color: #1a1a1a; |
|
|
|
} |
|
|
|
|
|
|
|
ul { |
|
|
|
margin: 0; |
|
|
|
padding-left: 20px; |
|
|
|
|
|
|
|
li { |
|
|
|
color: #666; |
|
|
|
font-size: 13px; |
|
|
|
line-height: 1.6; |
|
|
|
margin-bottom: 4px; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |
|
|
|