|
|
|
@ -74,7 +74,16 @@ import {
|
|
|
|
|
createDropdown,
|
|
|
|
|
Collection,
|
|
|
|
|
addListToDropdown,
|
|
|
|
|
View,
|
|
|
|
|
// LabeledFieldView,
|
|
|
|
|
ContextualBalloon,
|
|
|
|
|
// createLabeledInputText
|
|
|
|
|
// InputTextView
|
|
|
|
|
Model,
|
|
|
|
|
ListView,
|
|
|
|
|
ListItemView
|
|
|
|
|
} from 'ckeditor5';
|
|
|
|
|
// import View from '@ckeditor/ckeditor5-ui/src/view';
|
|
|
|
|
import translations from 'ckeditor5/translations/zh-cn.js';
|
|
|
|
|
import { asBlob } from 'html-docx-js-typescript'
|
|
|
|
|
import { saveAs } from 'file-saver';
|
|
|
|
@ -85,7 +94,8 @@ import {
|
|
|
|
|
saveData,
|
|
|
|
|
markdown2html,
|
|
|
|
|
html2markdown,
|
|
|
|
|
getUserAILayoutConfig
|
|
|
|
|
getUserAILayoutConfig,
|
|
|
|
|
saveUserAILayoutConfig
|
|
|
|
|
} from './utils';
|
|
|
|
|
import mitt from 'mitt';
|
|
|
|
|
// 导出为docx插件
|
|
|
|
@ -130,24 +140,6 @@ class Export2Word extends Plugin {
|
|
|
|
|
return button;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 增加菜单栏? 不显示按钮
|
|
|
|
|
// editor.ui.extendMenuBar({
|
|
|
|
|
// menu: {
|
|
|
|
|
// menuId: 'export',
|
|
|
|
|
// label: '导出',
|
|
|
|
|
// groups: [
|
|
|
|
|
// {
|
|
|
|
|
// groupId: 'export',
|
|
|
|
|
// items: [
|
|
|
|
|
// 'ExportToWord'
|
|
|
|
|
// ]
|
|
|
|
|
// }
|
|
|
|
|
// ]
|
|
|
|
|
// },
|
|
|
|
|
// position: 'after:help'
|
|
|
|
|
// }
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -384,6 +376,877 @@ class SaveButton extends Plugin {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class SettingFormView extends View {
|
|
|
|
|
constructor(locale) {
|
|
|
|
|
super(locale);
|
|
|
|
|
|
|
|
|
|
// 创建所有配置项的集合
|
|
|
|
|
this._createDropdowns();
|
|
|
|
|
this._createButtons();
|
|
|
|
|
|
|
|
|
|
this.setTemplate({
|
|
|
|
|
tag: 'form',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-setting-form'],
|
|
|
|
|
tabindex: '-1'
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
tag: 'div',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-setting-form-content']
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
this._createSection('标题样式', [this.titleStyleDropdown]),
|
|
|
|
|
...this.headingStyleDropdowns.map((heading, index) =>
|
|
|
|
|
this._createSection(`${index + 1}级标题`, [heading.size, heading.style])
|
|
|
|
|
),
|
|
|
|
|
this._createSection('列表样式', [this.listStyleDropdown]),
|
|
|
|
|
this._createSection('正文样式', [this.bodyStyleDropdown]),
|
|
|
|
|
this._createSection('引用样式', [this.blockquoteStyleDropdown]),
|
|
|
|
|
this._createSection('代码块样式', [this.codeBlockStyleDropdown])
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tag: 'div',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-setting-form-actions']
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
this.saveButtonView,
|
|
|
|
|
this.cancelButtonView
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_createDropdowns() {
|
|
|
|
|
this.titleStyleDropdown = this._createDropdownWithStyle('文章标题样式', [
|
|
|
|
|
{ text: '样式1', value: 'title1', class: 'ck-title1' },
|
|
|
|
|
{ text: '样式2', value: 'title2', class: 'ck-title2' },
|
|
|
|
|
{ text: '样式3', value: 'title3', class: 'ck-title3' }
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
this.headingStyleDropdowns = [
|
|
|
|
|
{
|
|
|
|
|
size: this._createDropdownWithStyle('一级标题大小', [
|
|
|
|
|
{ text: 'H3', value: 'h3',class: 'ck-h3' },
|
|
|
|
|
{ text: 'H4', value: 'h4',class: 'ck-h4' },
|
|
|
|
|
{ text: 'H5', value: 'h5',class: 'ck-h5' }
|
|
|
|
|
]),
|
|
|
|
|
style: this._createDropdown('一级标题样式', [
|
|
|
|
|
{ text: '(一) (二)', value: 'heading1'},
|
|
|
|
|
{ text: '一、 二、', value: 'heading2'},
|
|
|
|
|
{ text: '1. 2.', value: 'heading3'},
|
|
|
|
|
{ text: '1) 2)', value: 'heading4'},
|
|
|
|
|
{ text: '第一章 第二章', value: 'heading5'},
|
|
|
|
|
{ text: '第一小节 第二小节', value: 'heading6'},
|
|
|
|
|
{ text: 'I. II.', value: 'heading7'},
|
|
|
|
|
{ text: 'A. B.', value: 'heading8'},
|
|
|
|
|
{ text: 'a. b.', value: 'heading9'},
|
|
|
|
|
{ text: 'i. ii.', value: 'heading10'},
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
size: this._createDropdownWithStyle('二级标题大小', [
|
|
|
|
|
{ text: 'H3', value: 'h3',class: 'ck-h3' },
|
|
|
|
|
{ text: 'H4', value: 'h4',class: 'ck-h4' },
|
|
|
|
|
{ text: 'H5', value: 'h5',class: 'ck-h5' }
|
|
|
|
|
]),
|
|
|
|
|
style: this._createDropdown('二级标题样式', [
|
|
|
|
|
{ text: '(一) (二)', value: 'heading1'},
|
|
|
|
|
{ text: '一、 二、', value: 'heading2'},
|
|
|
|
|
{ text: '1. 2.', value: 'heading3'},
|
|
|
|
|
{ text: '1) 2)', value: 'heading4'},
|
|
|
|
|
{ text: '第一章 第二章', value: 'heading5'},
|
|
|
|
|
{ text: '第一小节 第二小节', value: 'heading6'},
|
|
|
|
|
{ text: 'I. II.', value: 'heading7'},
|
|
|
|
|
{ text: 'A. B.', value: 'heading8'},
|
|
|
|
|
{ text: 'a. b.', value: 'heading9'},
|
|
|
|
|
{ text: 'i. ii.', value: 'heading10'},
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
size: this._createDropdownWithStyle('三级标题大小', [
|
|
|
|
|
{ text: 'H3', value: 'h3',class: 'ck-h3' },
|
|
|
|
|
{ text: 'H4', value: 'h4',class: 'ck-h4' },
|
|
|
|
|
{ text: 'H5', value: 'h5',class: 'ck-h5' }
|
|
|
|
|
]),
|
|
|
|
|
style: this._createDropdown('三级标题样式', [
|
|
|
|
|
{ text: '(一) (二)', value: 'heading1'},
|
|
|
|
|
{ text: '一、 二、', value: 'heading2'},
|
|
|
|
|
{ text: '1. 2.', value: 'heading3'},
|
|
|
|
|
{ text: '1) 2)', value: 'heading4'},
|
|
|
|
|
{ text: '第一章 第二章', value: 'heading5'},
|
|
|
|
|
{ text: '第一小节 第二小节', value: 'heading6'},
|
|
|
|
|
{ text: 'I. II.', value: 'heading7'},
|
|
|
|
|
{ text: 'A. B.', value: 'heading8'},
|
|
|
|
|
{ text: 'a. b.', value: 'heading9'},
|
|
|
|
|
{ text: 'i. ii.', value: 'heading10'},
|
|
|
|
|
])
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
this.listStyleDropdown = this._createDropdownWithList('列表样式', [
|
|
|
|
|
{ text: '实心圆点', value: 'disc',class: 'ck-list-disc' },
|
|
|
|
|
{ text: '空心圆圈', value: 'circle', class: 'ck-list-circle' },
|
|
|
|
|
{ text: '方块', value: 'square', class: 'ck-list-square' },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
this.bodyStyleDropdown = this._createDropdownWithStyle('正文样式', [
|
|
|
|
|
{ text: '默认', value: 'normal-text', class: 'ck-normal-text' },
|
|
|
|
|
{ text: '四号宋体', value: 'songti-text', class: 'ck-songti-text' },
|
|
|
|
|
{ text: '五号楷体', value: 'kaiti-text', class: 'ck-kaiti-text' },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
this.blockquoteStyleDropdown = this._createDropdownWithSideQuote('引用样式', [
|
|
|
|
|
{ text: '侧引', value: 'side-quote',class: 'ck-side-quote' },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
this.codeBlockStyleDropdown = this._createDropdownWithCodeBlock('代码块样式', [
|
|
|
|
|
{ text: '经典暗色', value: 'fancy-code fancy-code-dark',class: 'ck-fancy-code ck-fancy-code-dark' },
|
|
|
|
|
{ text: '明亮主题', value: 'fancy-code fancy-code-bright',class: 'ck-fancy-code ck-fancy-code-bright' },
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_createDropdown(label, items) {
|
|
|
|
|
const dropdown = createDropdown(this.locale);
|
|
|
|
|
const listView = new ListView(this.locale);
|
|
|
|
|
|
|
|
|
|
dropdown.buttonView.set({
|
|
|
|
|
label,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: 'ck-dropdown-button'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 创建列表项集合
|
|
|
|
|
const itemsList = new Collection();
|
|
|
|
|
for (const item of items) {
|
|
|
|
|
const listItem = new ListItemView(this.locale);
|
|
|
|
|
listItem.children.add(new ButtonView(this.locale));
|
|
|
|
|
listItem.children.first.set({
|
|
|
|
|
label: item.text,
|
|
|
|
|
withText: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 绑定点击事件
|
|
|
|
|
listItem.children.first.on('execute', () => {
|
|
|
|
|
dropdown.buttonView.label = item.text;
|
|
|
|
|
dropdown.value = item.value;
|
|
|
|
|
dropdown.isOpen = false;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
itemsList.add(listItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
listView.items.bindTo(itemsList).using(item => item);
|
|
|
|
|
dropdown.panelView.children.add(listView);
|
|
|
|
|
|
|
|
|
|
return dropdown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_createDropdownWithStyle(label, items) {
|
|
|
|
|
const dropdown = createDropdown(this.locale);
|
|
|
|
|
const listView = new ListView(this.locale);
|
|
|
|
|
|
|
|
|
|
dropdown.buttonView.set({
|
|
|
|
|
label,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: ['ck', 'ck-dropdown-button']
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const itemsList = new Collection();
|
|
|
|
|
for (const item of items) {
|
|
|
|
|
const listItem = new ListItemView(this.locale);
|
|
|
|
|
|
|
|
|
|
// 创建按钮视图
|
|
|
|
|
const buttonView = new ButtonView(this.locale);
|
|
|
|
|
|
|
|
|
|
// 创建预览模型
|
|
|
|
|
const previewModel = new Model({
|
|
|
|
|
label: item.text,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: ['ck', 'ck-style-preview', 'ck-style-preview--rich']
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置按钮视图的模板
|
|
|
|
|
buttonView.extendTemplate({
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-button', 'ck-style-preview']
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 创建预览内容视图
|
|
|
|
|
const previewContent = new View(this.locale);
|
|
|
|
|
previewContent.setTemplate({
|
|
|
|
|
tag: 'div',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: [
|
|
|
|
|
'ck',
|
|
|
|
|
'ck-style-preview-content',
|
|
|
|
|
item.class
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
tag: 'div',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck-style-preview-sample']
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
text: item.sample || item.text
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
buttonView.children.add(previewContent);
|
|
|
|
|
buttonView.bind('label').to(previewModel, 'label');
|
|
|
|
|
|
|
|
|
|
// 绑定点击事件
|
|
|
|
|
buttonView.on('execute', () => {
|
|
|
|
|
dropdown.buttonView.label = item.text;
|
|
|
|
|
dropdown.value = item.value;
|
|
|
|
|
dropdown.isOpen = false;
|
|
|
|
|
|
|
|
|
|
// 触发change事件
|
|
|
|
|
this.fire('change', {
|
|
|
|
|
value: item.value,
|
|
|
|
|
style: item.class
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
listItem.children.add(buttonView);
|
|
|
|
|
itemsList.add(listItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
listView.items.bindTo(itemsList).using(item => item);
|
|
|
|
|
dropdown.panelView.children.add(listView);
|
|
|
|
|
|
|
|
|
|
return dropdown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_createDropdownWithList(label, items) {
|
|
|
|
|
const dropdown = createDropdown(this.locale);
|
|
|
|
|
const listView = new ListView(this.locale);
|
|
|
|
|
|
|
|
|
|
dropdown.buttonView.set({
|
|
|
|
|
label,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: ['ck', 'ck-dropdown-button'],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const itemsList = new Collection();
|
|
|
|
|
for (const item of items) {
|
|
|
|
|
const listItem = new ListItemView(this.locale);
|
|
|
|
|
|
|
|
|
|
// 创建按钮视图
|
|
|
|
|
const buttonView = new ButtonView(this.locale);
|
|
|
|
|
|
|
|
|
|
// 创建预览模型
|
|
|
|
|
const previewModel = new Model({
|
|
|
|
|
label: item.text,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: ['ck', 'ck-style-preview', 'ck-style-preview--rich'],
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置按钮视图的模板
|
|
|
|
|
buttonView.extendTemplate({
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-button', 'ck-style-preview']
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 创建预览内容视图
|
|
|
|
|
const previewContent = new View(this.locale);
|
|
|
|
|
previewContent.setTemplate({
|
|
|
|
|
tag: 'ul',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: [
|
|
|
|
|
'ck',
|
|
|
|
|
'ck-style-preview-content',
|
|
|
|
|
// item.class
|
|
|
|
|
|
|
|
|
|
],
|
|
|
|
|
// item.value disc circle square
|
|
|
|
|
style: {
|
|
|
|
|
listStyleType: item.value
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
tag: 'li',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck-style-preview-sample']
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
text: item.sample || item.text
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
buttonView.children.add(previewContent);
|
|
|
|
|
buttonView.bind('label').to(previewModel, 'label');
|
|
|
|
|
|
|
|
|
|
// 绑定点击事件
|
|
|
|
|
buttonView.on('execute', () => {
|
|
|
|
|
dropdown.buttonView.label = item.text;
|
|
|
|
|
dropdown.value = item.value;
|
|
|
|
|
dropdown.isOpen = false;
|
|
|
|
|
|
|
|
|
|
// 触发change事件
|
|
|
|
|
this.fire('change', {
|
|
|
|
|
value: item.value,
|
|
|
|
|
style: item.class
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
listItem.children.add(buttonView);
|
|
|
|
|
itemsList.add(listItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
listView.items.bindTo(itemsList).using(item => item);
|
|
|
|
|
dropdown.panelView.children.add(listView);
|
|
|
|
|
|
|
|
|
|
return dropdown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_createDropdownWithSideQuote(label, items) {
|
|
|
|
|
const dropdown = createDropdown(this.locale);
|
|
|
|
|
const listView = new ListView(this.locale);
|
|
|
|
|
|
|
|
|
|
dropdown.buttonView.set({
|
|
|
|
|
label,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: ['ck', 'ck-dropdown-button']
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const itemsList = new Collection();
|
|
|
|
|
for (const item of items) {
|
|
|
|
|
const listItem = new ListItemView(this.locale);
|
|
|
|
|
|
|
|
|
|
// 创建按钮视图
|
|
|
|
|
const buttonView = new ButtonView(this.locale);
|
|
|
|
|
|
|
|
|
|
// 创建预览模型
|
|
|
|
|
const previewModel = new Model({
|
|
|
|
|
label: item.text,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: ['ck', 'ck-style-preview', 'ck-style-preview--rich']
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置按钮视图的模板
|
|
|
|
|
buttonView.extendTemplate({
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-button', 'ck-style-preview']
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 创建预览内容视图
|
|
|
|
|
const previewContent = new View(this.locale);
|
|
|
|
|
previewContent.setTemplate({
|
|
|
|
|
tag: 'div',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: [
|
|
|
|
|
'ck',
|
|
|
|
|
'ck-style-preview-content',
|
|
|
|
|
item.class
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
tag: 'blockquote',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck-style-preview-sample']
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
tag: 'p',
|
|
|
|
|
// text: item.sample || item.text
|
|
|
|
|
// text: '引用内容'
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
text: item.sample || item.text
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
buttonView.children.add(previewContent);
|
|
|
|
|
buttonView.bind('label').to(previewModel, 'label');
|
|
|
|
|
|
|
|
|
|
// 绑定点击事件
|
|
|
|
|
buttonView.on('execute', () => {
|
|
|
|
|
dropdown.buttonView.label = item.text;
|
|
|
|
|
dropdown.value = item.value;
|
|
|
|
|
dropdown.isOpen = false;
|
|
|
|
|
|
|
|
|
|
// 触发change事件
|
|
|
|
|
this.fire('change', {
|
|
|
|
|
value: item.value,
|
|
|
|
|
style: item.class
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
listItem.children.add(buttonView);
|
|
|
|
|
itemsList.add(listItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
listView.items.bindTo(itemsList).using(item => item);
|
|
|
|
|
dropdown.panelView.children.add(listView);
|
|
|
|
|
|
|
|
|
|
return dropdown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_createDropdownWithCodeBlock(label, items) {
|
|
|
|
|
const dropdown = createDropdown(this.locale);
|
|
|
|
|
const listView = new ListView(this.locale);
|
|
|
|
|
|
|
|
|
|
dropdown.buttonView.set({
|
|
|
|
|
label,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: ['ck', 'ck-dropdown-button']
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const itemsList = new Collection();
|
|
|
|
|
for (const item of items) {
|
|
|
|
|
const listItem = new ListItemView(this.locale);
|
|
|
|
|
|
|
|
|
|
// 创建按钮视图
|
|
|
|
|
const buttonView = new ButtonView(this.locale);
|
|
|
|
|
|
|
|
|
|
// 创建预览模型
|
|
|
|
|
const previewModel = new Model({
|
|
|
|
|
label: item.text,
|
|
|
|
|
withText: true,
|
|
|
|
|
class: ['ck', 'ck-style-preview', 'ck-style-preview--rich']
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置按钮视图的模板
|
|
|
|
|
buttonView.extendTemplate({
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-button', 'ck-style-preview']
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 创建预览内容视图
|
|
|
|
|
const previewContent = new View(this.locale);
|
|
|
|
|
previewContent.setTemplate({
|
|
|
|
|
tag: 'pre',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: [
|
|
|
|
|
'ck',
|
|
|
|
|
'ck-style-preview-content',
|
|
|
|
|
item.class
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
tag: 'code',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck-style-preview-sample']
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
text: item.sample || item.text
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
buttonView.children.add(previewContent);
|
|
|
|
|
buttonView.bind('label').to(previewModel, 'label');
|
|
|
|
|
|
|
|
|
|
// 绑定点击事件
|
|
|
|
|
buttonView.on('execute', () => {
|
|
|
|
|
dropdown.buttonView.label = item.text;
|
|
|
|
|
dropdown.value = item.value;
|
|
|
|
|
dropdown.isOpen = false;
|
|
|
|
|
|
|
|
|
|
// 触发change事件
|
|
|
|
|
this.fire('change', {
|
|
|
|
|
value: item.value,
|
|
|
|
|
style: item.class
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
listItem.children.add(buttonView);
|
|
|
|
|
itemsList.add(listItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
listView.items.bindTo(itemsList).using(item => item);
|
|
|
|
|
dropdown.panelView.children.add(listView);
|
|
|
|
|
|
|
|
|
|
return dropdown;
|
|
|
|
|
}
|
|
|
|
|
_injectStyles() {
|
|
|
|
|
const styles = `
|
|
|
|
|
.ck.ck-style-preview.ck-button {
|
|
|
|
|
width: 100%;
|
|
|
|
|
text-align: left;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
border: none;
|
|
|
|
|
background: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck.ck-style-preview.ck-button:hover {
|
|
|
|
|
background: var(--ck-color-list-button-hover-background);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck.ck-style-preview.ck-button.ck-on {
|
|
|
|
|
background: var(--ck-color-list-button-on-background);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck.ck-style-preview-content {
|
|
|
|
|
padding: 8px;
|
|
|
|
|
margin: 2px 0;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck-style-preview-sample {
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 标题样式预览 */
|
|
|
|
|
.ck.ck-style-preview-content.ck-title1 .ck-style-preview-sample {
|
|
|
|
|
font-family: '黑体';
|
|
|
|
|
font-size: 29.3px;
|
|
|
|
|
font-weight: 'bold';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck.ck-style-preview-content.ck-title2 .ck-style-preview-sample {
|
|
|
|
|
font-family: '黑体';
|
|
|
|
|
font-size: 24.4px;
|
|
|
|
|
font-weight: 'bold';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck.ck-style-preview-content.ck-title3 .ck-style-preview-sample {
|
|
|
|
|
font-family: '宋体';
|
|
|
|
|
font-size: 20.3px;
|
|
|
|
|
font-weight: 'bold';
|
|
|
|
|
}
|
|
|
|
|
/* 多级标题样式预览 */
|
|
|
|
|
.ck.ck-style-preview-content.ck-h3 .ck-style-preview-sample {
|
|
|
|
|
font-size: 1.17em; /* 大约 24px */
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-h4 .ck-style-preview-sample {
|
|
|
|
|
|
|
|
|
|
font-size: 1.00em;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-h5 .ck-style-preview-sample {
|
|
|
|
|
font-size: 0.83em;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 引用样式预览 */
|
|
|
|
|
.ck.ck-style-preview-content.ck-side-quote .ck-style-preview-sample {
|
|
|
|
|
border-left: 4px solid #e74c3c;
|
|
|
|
|
padding-left: 16px;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck.ck-style-preview-content.ck-simple-quote .ck-style-preview-sample {
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck.ck-style-preview-content.ck-dark-quote .ck-style-preview-sample {
|
|
|
|
|
background: #2c3e50;
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 正文样式预览 */
|
|
|
|
|
.ck.ck-style-preview-content.ck-normal-text .ck-style-preview-sample {
|
|
|
|
|
font-family: '宋体';
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
overflow-wrap: break-word;
|
|
|
|
|
text-indent: 2em;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-songti-text .ck-style-preview-sample {
|
|
|
|
|
font-family: '宋体';
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
overflow-wrap: break-word;
|
|
|
|
|
text-indent: 2em;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-kaiti-text .ck-style-preview-sample {
|
|
|
|
|
font-family: '楷体';
|
|
|
|
|
font-size: 10.5px;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
overflow-wrap: break-word;
|
|
|
|
|
text-indent: 2em;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 块引用样式预览 */
|
|
|
|
|
.ck.ck-style-preview-content.ck-side-quote .ck-style-preview-sample {
|
|
|
|
|
font-family: 'Oswald';
|
|
|
|
|
font-style: normal;
|
|
|
|
|
float: right;
|
|
|
|
|
width: 35%;
|
|
|
|
|
position: relative;
|
|
|
|
|
border: 0;
|
|
|
|
|
overflow: visible;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
margin-left: 1em;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-side-quote .ck-style-preview-sample::before {
|
|
|
|
|
content: '“';
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: -37px;
|
|
|
|
|
left: -10px;
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 200px;
|
|
|
|
|
color: #e7e7e7;
|
|
|
|
|
z-index: -1;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-side-quote .ck-style-preview-sample p{
|
|
|
|
|
font-size: 2em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ck.ck-style-preview-content.ck-side-quote .ck-style-preview-sample p:last-child:not(:first-child){
|
|
|
|
|
font-size: 1.3em;
|
|
|
|
|
text-align: right;
|
|
|
|
|
color: #555;
|
|
|
|
|
}
|
|
|
|
|
/* 代码块样式预览 */
|
|
|
|
|
.ck.ck-style-preview-content.ck-fancy-code {
|
|
|
|
|
border: 0;
|
|
|
|
|
margin-left: 2em;
|
|
|
|
|
margin-right: 2em;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-fancy-code::before{
|
|
|
|
|
content: '';
|
|
|
|
|
display: block;
|
|
|
|
|
height: 13px;
|
|
|
|
|
background: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1NCAxMyI+CiAgPGNpcmNsZSBjeD0iNi41IiBjeT0iNi41IiByPSI2LjUiIGZpbGw9IiNGMzZCNUMiLz4KICA8Y2lyY2xlIGN4PSIyNi41IiBjeT0iNi41IiByPSI2LjUiIGZpbGw9IiNGOUJFNEQiLz4KICA8Y2lyY2xlIGN4PSI0Ny41IiBjeT0iNi41IiByPSI2LjUiIGZpbGw9IiM1NkM0NTMiLz4KPC9zdmc+Cg==);
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
background-repeat: no-repeat;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-fancy-code-dark{
|
|
|
|
|
background: #272822;
|
|
|
|
|
color: #fff;
|
|
|
|
|
box-shadow: 5px 5px 0 #0000001f;
|
|
|
|
|
}
|
|
|
|
|
.ck.ck-style-preview-content.ck-fancy-code-bright{
|
|
|
|
|
background: #dddfe0;
|
|
|
|
|
color: #000;
|
|
|
|
|
box-shadow: 5px 5px 0 #b3b3b3;
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
// 创建style元素并添加到编辑器容器中
|
|
|
|
|
const styleElement = document.createElement('style');
|
|
|
|
|
styleElement.textContent = styles;
|
|
|
|
|
this.element.ownerDocument.head.appendChild(styleElement);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_createButtons() {
|
|
|
|
|
this.saveButtonView = new ButtonView(this.locale);
|
|
|
|
|
this.saveButtonView.set({
|
|
|
|
|
label: '保存',
|
|
|
|
|
withText: true,
|
|
|
|
|
class: 'ck-button-save'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.cancelButtonView = new ButtonView(this.locale);
|
|
|
|
|
this.cancelButtonView.set({
|
|
|
|
|
label: '取消',
|
|
|
|
|
withText: true,
|
|
|
|
|
class: 'ck-button-cancel'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_createSection(label, children) {
|
|
|
|
|
return {
|
|
|
|
|
tag: 'div',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-setting-section']
|
|
|
|
|
},
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
tag: 'label',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-setting-section-label']
|
|
|
|
|
},
|
|
|
|
|
children: [label]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tag: 'div',
|
|
|
|
|
attributes: {
|
|
|
|
|
class: ['ck', 'ck-setting-section-content']
|
|
|
|
|
},
|
|
|
|
|
children
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getData() {
|
|
|
|
|
return {
|
|
|
|
|
titleStyle: {
|
|
|
|
|
option: this.titleStyleDropdown.value
|
|
|
|
|
},
|
|
|
|
|
headingStyle: {
|
|
|
|
|
option: this.headingStyleDropdowns.map(heading => [
|
|
|
|
|
heading.size.value,
|
|
|
|
|
heading.style.value
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
listStyle: {
|
|
|
|
|
option: this.listStyleDropdown.value
|
|
|
|
|
},
|
|
|
|
|
bodyStyle: {
|
|
|
|
|
option: this.bodyStyleDropdown.value
|
|
|
|
|
},
|
|
|
|
|
blockquoteStyle: {
|
|
|
|
|
option: this.blockquoteStyleDropdown.value
|
|
|
|
|
},
|
|
|
|
|
codeBlockStyle: {
|
|
|
|
|
option: this.codeBlockStyleDropdown.value.split(' ')
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
super.render();
|
|
|
|
|
|
|
|
|
|
// 注入样式
|
|
|
|
|
this._injectStyles();
|
|
|
|
|
|
|
|
|
|
// 阻止表单提交
|
|
|
|
|
this.element.addEventListener('submit', evt => {
|
|
|
|
|
evt.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
focus() {
|
|
|
|
|
this.titleStyleDropdown.focus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
class SettingButton extends Plugin {
|
|
|
|
|
static get requires() {
|
|
|
|
|
return [ContextualBalloon];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init() {
|
|
|
|
|
console.log('SettingButton plugin initialized'); // 调试日志
|
|
|
|
|
|
|
|
|
|
const editor = this.editor;
|
|
|
|
|
this._balloon = editor.plugins.get(ContextualBalloon);
|
|
|
|
|
this._formView = new SettingFormView(editor.locale);
|
|
|
|
|
|
|
|
|
|
// 注册按钮
|
|
|
|
|
editor.ui.componentFactory.add('SettingButton', () => {
|
|
|
|
|
const button = new ButtonView();
|
|
|
|
|
|
|
|
|
|
button.set({
|
|
|
|
|
label: '设置',
|
|
|
|
|
icon: '<svg t="1736071158607" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3024" width="200" height="200"><path d="M899.2 379.2a439.68 439.68 0 0 0-19.52-47.04 137.28 137.28 0 0 0-187.84-187.84 439.68 439.68 0 0 0-47.04-19.52 137.28 137.28 0 0 0-265.6 0 439.68 439.68 0 0 0-47.04 19.52 137.28 137.28 0 0 0-187.84 187.84 439.68 439.68 0 0 0-19.52 47.04 137.28 137.28 0 0 0 0 265.6 439.68 439.68 0 0 0 19.52 47.04 137.28 137.28 0 0 0 187.84 187.84 439.68 439.68 0 0 0 47.04 19.52 137.28 137.28 0 0 0 265.6 0 439.68 439.68 0 0 0 47.04-19.52 137.28 137.28 0 0 0 187.84-187.84 439.68 439.68 0 0 0 19.52-47.04 137.28 137.28 0 0 0 0-265.6z" p-id="3025"></path><path d="M512 310.4a201.6 201.6 0 1 0 201.6 201.6A201.92 201.92 0 0 0 512 310.4z m0 320a118.4 118.4 0 1 1 118.4-118.4 118.4 118.4 0 0 1-118.4 118.4z" p-id="3026"></path></svg>',
|
|
|
|
|
tooltip: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 点击按钮时显示表单
|
|
|
|
|
button.on('execute', () => {
|
|
|
|
|
console.log('Setting button clicked'); // 调试日志
|
|
|
|
|
this._showForm();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return button;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 绑定表单事件
|
|
|
|
|
this._formView.saveButtonView.on('execute', () => {
|
|
|
|
|
console.log('Saving settings:', this._formView.getData());
|
|
|
|
|
// 保存用户设置
|
|
|
|
|
saveUserAILayoutConfig(this._formView.getData())
|
|
|
|
|
this._hideForm();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this._formView.cancelButtonView.on('execute', () => {
|
|
|
|
|
console.log('Cancel button clicked'); // 调试日志
|
|
|
|
|
this._hideForm();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_showForm() {
|
|
|
|
|
if (this._isFormInPanel) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const targetElement = this.editor.ui.getEditableElement();
|
|
|
|
|
const positions = [
|
|
|
|
|
targetElement.getBoundingClientRect().top + window.scrollY,
|
|
|
|
|
targetElement.getBoundingClientRect().left + window.scrollX
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
this._balloon.add({
|
|
|
|
|
view: this._formView,
|
|
|
|
|
position: {
|
|
|
|
|
target: targetElement,
|
|
|
|
|
positions: [
|
|
|
|
|
(targetRect, balloonRect) => ({
|
|
|
|
|
top: positions[0] + 10,
|
|
|
|
|
left: positions[1] + targetRect.width / 2 - balloonRect.width / 2,
|
|
|
|
|
name: 'arrow_n'
|
|
|
|
|
})
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this._formView.focus();
|
|
|
|
|
this._isFormInPanel = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_hideForm() {
|
|
|
|
|
if (!this._isFormInPanel) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._balloon.remove(this._formView);
|
|
|
|
|
this._isFormInPanel = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
destroy() {
|
|
|
|
|
super.destroy();
|
|
|
|
|
this._formView.destroy();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AI 自动排版
|
|
|
|
|
async function aiformat() {
|
|
|
|
|
console.log("ai formatting")
|
|
|
|
@ -692,9 +1555,9 @@ class PicRecog extends Plugin {
|
|
|
|
|
// 配置CKEditor5
|
|
|
|
|
// DFZ
|
|
|
|
|
//
|
|
|
|
|
function setConfig() {
|
|
|
|
|
async function setConfig() {
|
|
|
|
|
// 获取用户的样式配置
|
|
|
|
|
const userConfig = getUserConfigFromBackend();
|
|
|
|
|
const userConfig = await getUserConfigFromBackend();
|
|
|
|
|
return {
|
|
|
|
|
toolbar: {
|
|
|
|
|
items: [
|
|
|
|
@ -725,7 +1588,7 @@ function setConfig() {
|
|
|
|
|
'numberedList',
|
|
|
|
|
'outdent',
|
|
|
|
|
'indent',
|
|
|
|
|
'|', 'ExportToWord', 'ExportToPDF', 'RefineDoc', 'SideBar', 'SaveButton', 'AiFormat'
|
|
|
|
|
'|', 'ExportToWord', 'ExportToPDF', 'RefineDoc', 'SideBar', 'SaveButton', 'AiFormat','SettingButton'
|
|
|
|
|
],
|
|
|
|
|
shouldNotGroupWhenFull: true
|
|
|
|
|
},
|
|
|
|
@ -799,7 +1662,7 @@ function setConfig() {
|
|
|
|
|
TodoList,
|
|
|
|
|
Underline,
|
|
|
|
|
Undo,
|
|
|
|
|
Export2Word, RefineDoc, Export2PDF, ToggleSideBar, SaveButton, AiFormat, PicRecog
|
|
|
|
|
Export2Word, RefineDoc, Export2PDF, ToggleSideBar, SaveButton,SettingButton, AiFormat, PicRecog
|
|
|
|
|
],
|
|
|
|
|
balloonToolbar: ['bold', 'italic', '|', 'link', 'insertImage', '|', 'bulletedList', 'numberedList'],
|
|
|
|
|
//自定义设置字体
|
|
|
|
|