Initial commit
This commit is contained in:
393
app/components/common/TextEditor.vue
Normal file
393
app/components/common/TextEditor.vue
Normal file
@@ -0,0 +1,393 @@
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div ref="el"></div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import 'ckeditor5/ckeditor5.css';
|
||||
import 'ckeditor5-premium-features/ckeditor5-premium-features.css';
|
||||
|
||||
const el = ref(null);
|
||||
let editor = null;
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const content = ref(props.modelValue || '');
|
||||
|
||||
onMounted(async () => {
|
||||
if (!process.client) return;
|
||||
|
||||
const {
|
||||
ClassicEditor,
|
||||
AccessibilityHelp,
|
||||
Autoformat,
|
||||
AutoImage,
|
||||
AutoLink,
|
||||
Autosave,
|
||||
BalloonToolbar,
|
||||
BlockQuote,
|
||||
BlockToolbar,
|
||||
Bold,
|
||||
CKBox,
|
||||
CKBoxImageEdit,
|
||||
CloudServices,
|
||||
Essentials,
|
||||
FontBackgroundColor,
|
||||
FontColor,
|
||||
FontFamily,
|
||||
FontSize,
|
||||
Heading,
|
||||
Highlight,
|
||||
ImageBlock,
|
||||
ImageCaption,
|
||||
ImageInline,
|
||||
ImageInsert,
|
||||
ImageInsertViaUrl,
|
||||
ImageResize,
|
||||
ImageStyle,
|
||||
ImageTextAlternative,
|
||||
ImageToolbar,
|
||||
ImageUpload,
|
||||
Indent,
|
||||
IndentBlock,
|
||||
Italic,
|
||||
Link,
|
||||
LinkImage,
|
||||
List,
|
||||
ListProperties,
|
||||
MediaEmbed,
|
||||
Mention,
|
||||
PageBreak,
|
||||
Paragraph,
|
||||
PasteFromOffice,
|
||||
PictureEditing,
|
||||
RemoveFormat,
|
||||
SelectAll,
|
||||
SpecialCharacters,
|
||||
SpecialCharactersArrows,
|
||||
SpecialCharactersCurrency,
|
||||
SpecialCharactersEssentials,
|
||||
SpecialCharactersLatin,
|
||||
SpecialCharactersMathematical,
|
||||
SpecialCharactersText,
|
||||
Subscript,
|
||||
Superscript,
|
||||
Table,
|
||||
TableCaption,
|
||||
TableCellProperties,
|
||||
GeneralHtmlSupport,
|
||||
TableColumnResize,
|
||||
TableProperties,
|
||||
TableToolbar,
|
||||
TextTransformation,
|
||||
TodoList,
|
||||
Underline,
|
||||
Alignment,
|
||||
Undo,
|
||||
} = await import('ckeditor5');
|
||||
const { ExportPdf, ExportWord, ImportWord } = await import('ckeditor5-premium-features');
|
||||
|
||||
editor = await ClassicEditor.create(el.value, {
|
||||
// licenseKey: 'GPL',
|
||||
|
||||
plugins: [
|
||||
AccessibilityHelp,
|
||||
Autoformat,
|
||||
AutoImage,
|
||||
AutoLink,
|
||||
Autosave,
|
||||
BalloonToolbar,
|
||||
BlockQuote,
|
||||
BlockToolbar,
|
||||
Bold,
|
||||
CKBox,
|
||||
CKBoxImageEdit,
|
||||
CloudServices,
|
||||
Essentials,
|
||||
ExportPdf,
|
||||
ExportWord,
|
||||
FontBackgroundColor,
|
||||
FontColor,
|
||||
FontFamily,
|
||||
FontSize,
|
||||
Heading,
|
||||
Highlight,
|
||||
ImageBlock,
|
||||
ImageCaption,
|
||||
ImageInline,
|
||||
ImageInsert,
|
||||
ImageInsertViaUrl,
|
||||
ImageResize,
|
||||
ImageStyle,
|
||||
ImageTextAlternative,
|
||||
ImageToolbar,
|
||||
ImageUpload,
|
||||
ImportWord,
|
||||
Indent,
|
||||
IndentBlock,
|
||||
Italic,
|
||||
Link,
|
||||
LinkImage,
|
||||
List,
|
||||
ListProperties,
|
||||
MediaEmbed,
|
||||
Mention,
|
||||
PageBreak,
|
||||
Paragraph,
|
||||
PasteFromOffice,
|
||||
PictureEditing,
|
||||
RemoveFormat,
|
||||
SelectAll,
|
||||
SpecialCharacters,
|
||||
SpecialCharactersArrows,
|
||||
SpecialCharactersCurrency,
|
||||
SpecialCharactersEssentials,
|
||||
SpecialCharactersLatin,
|
||||
SpecialCharactersMathematical,
|
||||
SpecialCharactersText,
|
||||
Subscript,
|
||||
Superscript,
|
||||
Table,
|
||||
TableCaption,
|
||||
TableCellProperties,
|
||||
TableColumnResize,
|
||||
TableProperties,
|
||||
TableToolbar,
|
||||
TextTransformation,
|
||||
TodoList,
|
||||
Underline,
|
||||
Alignment,
|
||||
GeneralHtmlSupport,
|
||||
Undo,
|
||||
],
|
||||
|
||||
toolbar: [
|
||||
'undo',
|
||||
'redo',
|
||||
'|',
|
||||
'importWord',
|
||||
'exportWord',
|
||||
'exportPdf',
|
||||
'|',
|
||||
'heading',
|
||||
'|',
|
||||
'fontSize',
|
||||
'fontFamily',
|
||||
'fontColor',
|
||||
'fontBackgroundColor',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'subscript',
|
||||
'superscript',
|
||||
'removeFormat',
|
||||
'|',
|
||||
'alignment', // 👈 THÊM
|
||||
'|',
|
||||
'specialCharacters',
|
||||
'pageBreak',
|
||||
'link',
|
||||
'insertImage',
|
||||
'insertImageViaUrl',
|
||||
'ckbox',
|
||||
'mediaEmbed',
|
||||
'insertTable',
|
||||
'highlight',
|
||||
'blockQuote',
|
||||
'|',
|
||||
'bulletedList',
|
||||
'numberedList',
|
||||
'todoList',
|
||||
'outdent',
|
||||
'indent',
|
||||
],
|
||||
exportPdf: {
|
||||
stylesheets: [
|
||||
'https://cdn.ckeditor.com/ckeditor5/43.2.0/ckeditor5.css',
|
||||
'https://cdn.ckeditor.com/ckeditor5-premium-features/43.2.0/ckeditor5-premium-features.css',
|
||||
],
|
||||
fileName: 'export-pdf-demo.pdf',
|
||||
converterOptions: {
|
||||
format: 'Tabloid',
|
||||
margin_top: '20mm',
|
||||
margin_bottom: '20mm',
|
||||
margin_right: '24mm',
|
||||
margin_left: '24mm',
|
||||
page_orientation: 'portrait',
|
||||
},
|
||||
},
|
||||
exportWord: {
|
||||
stylesheets: [
|
||||
'https://cdn.ckeditor.com/ckeditor5/43.2.0/ckeditor5.css',
|
||||
'https://cdn.ckeditor.com/ckeditor5-premium-features/43.2.0/ckeditor5-premium-features.css',
|
||||
],
|
||||
fileName: 'export-word-demo.docx',
|
||||
converterOptions: {
|
||||
document: {
|
||||
orientation: 'portrait',
|
||||
size: 'Tabloid',
|
||||
margins: {
|
||||
top: '20mm',
|
||||
bottom: '20mm',
|
||||
right: '24mm',
|
||||
left: '24mm',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
balloonToolbar: ['bold', 'italic','fontFamily','fontSize', '|', 'alignment', 'link', 'insertImage', '|', 'bulletedList', 'numberedList'],
|
||||
blockToolbar: [
|
||||
'fontSize',
|
||||
'fontColor',
|
||||
'fontBackgroundColor',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'|',
|
||||
'link',
|
||||
'insertImage',
|
||||
'insertTable',
|
||||
'|',
|
||||
'bulletedList',
|
||||
'numberedList',
|
||||
'outdent',
|
||||
'indent',
|
||||
],
|
||||
fontFamily: {
|
||||
supportAllValues: true,
|
||||
},
|
||||
fontSize: {
|
||||
options: [10, 12,13, 14, 'default', 18, 20, 22],
|
||||
supportAllValues: true,
|
||||
},
|
||||
heading: {
|
||||
options: [
|
||||
{
|
||||
model: 'paragraph',
|
||||
title: 'Paragraph',
|
||||
class: 'ck-heading_paragraph',
|
||||
},
|
||||
{
|
||||
model: 'heading1',
|
||||
view: 'h1',
|
||||
title: 'Heading 1',
|
||||
class: 'ck-heading_heading1',
|
||||
},
|
||||
{
|
||||
model: 'heading2',
|
||||
view: 'h2',
|
||||
title: 'Heading 2',
|
||||
class: 'ck-heading_heading2',
|
||||
},
|
||||
{
|
||||
model: 'heading3',
|
||||
view: 'h3',
|
||||
title: 'Heading 3',
|
||||
class: 'ck-heading_heading3',
|
||||
},
|
||||
{
|
||||
model: 'heading4',
|
||||
view: 'h4',
|
||||
title: 'Heading 4',
|
||||
class: 'ck-heading_heading4',
|
||||
},
|
||||
{
|
||||
model: 'heading5',
|
||||
view: 'h5',
|
||||
title: 'Heading 5',
|
||||
class: 'ck-heading_heading5',
|
||||
},
|
||||
{
|
||||
model: 'heading6',
|
||||
view: 'h6',
|
||||
title: 'Heading 6',
|
||||
class: 'ck-heading_heading6',
|
||||
},
|
||||
],
|
||||
},
|
||||
image: {
|
||||
toolbar: [
|
||||
'toggleImageCaption',
|
||||
'imageTextAlternative',
|
||||
'|',
|
||||
'imageStyle:inline',
|
||||
'imageStyle:wrapText',
|
||||
'imageStyle:breakText',
|
||||
'|',
|
||||
'resizeImage',
|
||||
'|',
|
||||
'ckboxImageEdit',
|
||||
],
|
||||
},
|
||||
link: {
|
||||
addTargetToExternalLinks: true,
|
||||
defaultProtocol: 'https://',
|
||||
decorators: {
|
||||
toggleDownloadable: {
|
||||
mode: 'manual',
|
||||
label: 'Downloadable',
|
||||
attributes: {
|
||||
download: 'file',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
list: {
|
||||
properties: {
|
||||
styles: true,
|
||||
startIndex: true,
|
||||
reversed: true,
|
||||
},
|
||||
},
|
||||
mention: {
|
||||
feeds: [
|
||||
{
|
||||
marker: '@',
|
||||
feed: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
menuBar: {
|
||||
isVisible: true,
|
||||
},
|
||||
table: {
|
||||
contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties'],
|
||||
},
|
||||
alignment: {
|
||||
options: ['left', 'center', 'right', 'justify'],
|
||||
},
|
||||
});
|
||||
|
||||
// set data ban đầu
|
||||
editor.setData(props.modelValue || '');
|
||||
|
||||
// khi thay đổi → emit ra ngoài
|
||||
editor.model.document.on('change:data', () => {
|
||||
const data = editor.getData();
|
||||
emit('update:modelValue', data);
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (editor && val !== editor.getData()) {
|
||||
editor.setData(val || '');
|
||||
}
|
||||
},
|
||||
);
|
||||
onBeforeUnmount(async () => {
|
||||
if (editor) {
|
||||
await editor.destroy();
|
||||
editor = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user