增加保存和获取用户自定义样式、保存和获取用户自动排版设置

master
joefalmko 7 months ago
parent 7e59848958
commit fed8981ebb

@ -0,0 +1,213 @@
package web
import (
"github.com/gin-gonic/gin"
"goskeleton/app/global/consts"
"goskeleton/app/global/variable"
"goskeleton/app/utils/response"
"os"
"encoding/json"
"strings"
)
type Editor struct {
}
func (e *Editor) UserStyleSave(context *gin.Context) {
// 获取用户输入
userId, _ := context.Get(consts.ValidatorPrefix + "user_name")
styleName, _ := context.Get(consts.ValidatorPrefix + "style_name")
elementName, _ := context.Get(consts.ValidatorPrefix + "element_name")
styleClasses, _ := context.Get(consts.ValidatorPrefix + "style_classes")
styleContent, _ := context.Get(consts.ValidatorPrefix + "style_content")
// 转换 style classes
var styleClassesStr []string
for _, v := range styleClasses.([]interface{}) {
styleClassesStr = append(styleClassesStr, v.(string))
}
// 准备新的样式数据
rowData := map[string]interface{}{
"style_name": styleName.(string),
"element_name": elementName.(string),
"style_classes": styleClassesStr,
"style_content": styleContent.(string),
}
userStyleDefinitionPath := variable.BasePath + variable.ConfigYml.GetString("Style.UserStyleSavePath") + "/" + userId.(string) + ".json"
var existingStyles []map[string]interface{}
// 检查文件是否存在
if _, err := os.Stat(userStyleDefinitionPath); os.IsNotExist(err) {
// 文件不存在,创建新的数组
existingStyles = []map[string]interface{}{rowData}
} else {
// 文件存在,读取现有内容
existingData, err := os.ReadFile(userStyleDefinitionPath)
if err != nil {
response.ErrorSystem(context, "无法读取样式文件", "")
return
}
// 解析现有的 JSON 数据
if err := json.Unmarshal(existingData, &existingStyles); err != nil {
response.ErrorSystem(context, "无法解析样式文件", "")
return
}
// 查找是否存在相同的 style_name
found := false
for i, style := range existingStyles {
if style["style_name"] == styleName.(string) {
// 找到相同的 style_name更新内容
existingStyles[i] = rowData
found = true
break
}
}
// 如果没有找到相同的 style_name则追加新的样式
if !found {
existingStyles = append(existingStyles, rowData)
}
}
// 将更新后的数据转换为 JSON并使用缩进格式
data, err := json.MarshalIndent(existingStyles, "", " ")
if err != nil {
response.ErrorSystem(context, "无法序列化样式数据", "")
return
}
// 写入文件(覆盖原文件)
if err := os.WriteFile(userStyleDefinitionPath, data, 0644); err != nil {
response.ErrorSystem(context, "无法写入样式文件", "")
return
}
response.Success(context, "UserStyleSave", "")
}
func (e *Editor) UserStyleGet(context *gin.Context) {
userId, _ := context.Get(consts.ValidatorPrefix + "user_name")
userStyleDefinitionPath := variable.BasePath + variable.ConfigYml.GetString("Style.UserStyleSavePath") + "/" + userId.(string) + ".json"
// 检查文件是否存在
if _, err := os.Stat(userStyleDefinitionPath); os.IsNotExist(err) {
// 如果文件不存在,返回空数组而不是错误
response.Success(context, "UserStyleGet", []map[string]interface{}{})
return
}
// 读取文件内容
existingData, err := os.ReadFile(userStyleDefinitionPath)
if err != nil {
response.ErrorSystem(context, "无法读取样式文件", "")
return
}
// 使用与 UserStyleSave 相同的数据类型
var existingStyles []map[string]interface{}
if err := json.Unmarshal(existingData, &existingStyles); err != nil {
response.ErrorSystem(context, "无法解析样式文件", "")
return
}
response.Success(context, "UserStyleGet", existingStyles)
}
func (e *Editor) AiFormatConfigSave(context *gin.Context) {
// 获取用户输入
id, _ := context.Get(consts.ValidatorPrefix + "user_name")
titleStyle, _ := context.Get(consts.ValidatorPrefix + "title_style")
headingStyle, _ := context.Get(consts.ValidatorPrefix + "heading_style")
listStyle, _ := context.Get(consts.ValidatorPrefix + "list_style")
bodyStyle, _ := context.Get(consts.ValidatorPrefix + "body_style")
blockquoteStyle, _ := context.Get(consts.ValidatorPrefix + "blockquote_style")
codeblockStyle, _ := context.Get(consts.ValidatorPrefix + "codeblock_style")
// 处理 headingStyle
var headingStyleArray [][]string
if headingStr, ok := headingStyle.(string); ok {
// 先按逗号分割
headings := strings.Split(headingStr, ",")
// 每两个元素组成一对
for i := 0; i < len(headings); i += 2 {
if i+1 < len(headings) {
pair := []string{headings[i], headings[i+1]}
headingStyleArray = append(headingStyleArray, pair)
}
}
}
// 处理 codeblockStyle
var codeblockStyleArray []string
if codeblockStr, ok := codeblockStyle.(string); ok {
codeblockStyleArray = strings.Split(codeblockStr, ",")
}
// 准备配置数据
rowData := map[string]interface{}{
"title_style": titleStyle.(string),
"heading_style": headingStyleArray, // 使用处理后的二维数组
"list_style": listStyle.(string),
"body_style": bodyStyle.(string),
"blockquote_style": blockquoteStyle.(string),
"codeblock_style": codeblockStyleArray, // 使用处理后的数组
}
aiFormatConfigPath := variable.BasePath + variable.ConfigYml.GetString("Style.UserFormatConfigSavePath") + "/" + id.(string) + ".json"
// 将数据转换为格式化的 JSON
data, err := json.MarshalIndent(rowData, "", " ")
if err != nil {
response.ErrorSystem(context, "无法序列化AI格式配置数据", "")
return
}
// 写入文件(覆盖原文件)
if err := os.WriteFile(aiFormatConfigPath, data, 0644); err != nil {
response.ErrorSystem(context, "无法写入AI格式配置文件", "")
return
}
response.Success(context, "AiFormatConfigSave", "")
}
func (e *Editor) AiFormatConfigGet(context *gin.Context) {
id, _ := context.Get(consts.ValidatorPrefix + "user_name")
aiFormatConfigPath := variable.BasePath + variable.ConfigYml.GetString("Style.UserFormatConfigSavePath") + "/" + id.(string) + ".json"
// 检查文件是否存在
if _, err := os.Stat(aiFormatConfigPath); os.IsNotExist(err) {
// 返回默认空配置而不是错误
defaultConfig := map[string]interface{}{
"title_style": "",
"heading_style": []string{},
"list_style": "",
"body_style": "",
"blockquote_style": "",
"codeblock_style": "",
}
response.Success(context, "AiFormatConfigGet", defaultConfig)
return
}
// 读取文件内容
existingData, err := os.ReadFile(aiFormatConfigPath)
if err != nil {
response.ErrorSystem(context, "无法读取AI格式配置文件", "")
return
}
// 解析 JSON 数据
var existingStyles map[string]interface{}
if err := json.Unmarshal(existingData, &existingStyles); err != nil {
response.ErrorSystem(context, "无法解析AI格式配置文件", "")
return
}
response.Success(context, "AiFormatConfigGet", existingStyles)
}

@ -18,6 +18,7 @@ func Next() gin.HandlerFunc {
// 放行所有OPTIONS方法
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusAccepted)
return
}
c.Next()
}

@ -10,6 +10,7 @@ import (
"goskeleton/app/http/validator/web/ai_recognition"
"goskeleton/app/http/validator/web/file"
"goskeleton/app/http/validator/web/users"
"goskeleton/app/http/validator/web/editor"
)
// 各个业务模块验证器必须进行注册(初始化),程序启动时会自动加载到容器
@ -91,4 +92,19 @@ func WebRegisterValidator() {
key = consts.ValidatorPrefix + "FolderGet"
containers.Set(key, file.FolderGet{})
// 用户配置相关
// 用户样式保存
key = consts.ValidatorPrefix + "UserStyleSave"
containers.Set(key, editor.UserStyleSave{})
// 用户样式获取
key = consts.ValidatorPrefix + "UserStyleGet"
containers.Set(key, editor.UserStyleGet{})
// AI格式配置保存
key = consts.ValidatorPrefix + "AiFormatConfigSave"
containers.Set(key, editor.AiFormatConfigSave{})
// AI格式配置获取
key = consts.ValidatorPrefix + "AiFormatConfigGet"
containers.Set(key, editor.AiFormatConfigGet{})
}

@ -0,0 +1,84 @@
package editor
import (
"github.com/gin-gonic/gin"
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
)
type UserStyleSave struct{
UserName string `form:"user_name" json:"user_name" `
StyleName string `form:"style_name" json:"style_name" `
ElementName string `form:"element_name" json:"element_name" `
StyleClasses []string `form:"style_classes" json:"style_classes"`
StyleContent string `form:"style_content" json:"style_content" `
}
func (u UserStyleSave) CheckParams(context *gin.Context) {
if err := context.ShouldBind(&u); err != nil {
response.ValidatorError(context, err)
return
}
extraAddBindDataContext := data_transfer.DataAddContext(u, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "UserStyleSave表单验证器json化失败", "")
} else {
(&web.Editor{}).UserStyleSave(extraAddBindDataContext)
}
}
type UserStyleGet struct{
UserName string `form:"user_name" json:"user_name" binding:"required,min=1"`
}
func (u UserStyleGet) CheckParams(context *gin.Context) {
if err := context.ShouldBind(&u); err != nil {
response.ValidatorError(context, err)
return
}
extraAddBindDataContext := data_transfer.DataAddContext(u, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "UserStyleGet表单验证器json化失败", "")
} else {
(&web.Editor{}).UserStyleGet(extraAddBindDataContext)
}
}
type AiFormatConfigSave struct{
UserName string `form:"user_name" json:"user_name" binding:"required,min=1"`
TitleStyle string `form:"title_style" json:"title_style" binding:"required,min=1"`
HeadingStyle string `form:"heading_style" json:"heading_style" binding:"required,min=1"`
ListStyle string `form:"list_style" json:"list_style" binding:"required,min=1"`
BodyStyle string `form:"body_style" json:"body_style" binding:"required,min=1"`
BlockquoteStyle string `form:"blockquote_style" json:"blockquote_style" binding:"required,min=1"`
CodeblockStyle string `form:"codeblock_style" json:"codeblock_style" binding:"required,min=1"`
}
func (u AiFormatConfigSave) CheckParams(context *gin.Context) {
if err := context.ShouldBind(&u); err != nil {
response.ValidatorError(context, err)
return
}
extraAddBindDataContext := data_transfer.DataAddContext(u, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "AiFormatConfigSave表单验证器json化失败", "")
} else {
(&web.Editor{}).AiFormatConfigSave(extraAddBindDataContext)
}
}
type AiFormatConfigGet struct{
UserName string `form:"user_name" json:"user_name" binding:"required,min=1"`
}
func (u AiFormatConfigGet) CheckParams(context *gin.Context) {
if err := context.ShouldBind(&u); err != nil {
response.ValidatorError(context, err)
return
}
extraAddBindDataContext := data_transfer.DataAddContext(u, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "AiFormatConfigGet表单验证器json化失败", "")
} else {
(&web.Editor{}).AiFormatConfigGet(extraAddBindDataContext)
}
}

@ -155,3 +155,7 @@ BaiduCE:
QianFanSecretKey: "cb812e1b6e56420ea858d160e1351869"
StyleGeneratePromptPath: "/storage/app/prompt/style_generate.prompt" # 生成样式的提示词保存路径
LayoutGeneratePromptPath: "/storage/app/prompt/layout_generate.prompt" # 生成布局的提示词保存路径
Style:
UserFormatConfigSavePath: "/storage/user_format_config"
UserStyleSavePath: "/storage/user_styles"

@ -87,6 +87,19 @@ func InitWebRouter_Co() *gin.Engine {
aiLayout.POST("layout_generate", validatorFactory.Create(consts.ValidatorPrefix+"LayoutGenerate"))
}
// 用户配置相关
userConfig := backend.Group("user_config/")
{
// 用户样式保存
userConfig.POST("user_style_save", validatorFactory.Create(consts.ValidatorPrefix+"UserStyleSave"))
// 用户样式获取
userConfig.POST("user_style_get", validatorFactory.Create(consts.ValidatorPrefix+"UserStyleGet"))
// AI格式配置保存
userConfig.POST("ai_format_config_save", validatorFactory.Create(consts.ValidatorPrefix+"AiFormatConfigSave"))
// AI格式配置获取
userConfig.POST("ai_format_config_get", validatorFactory.Create(consts.ValidatorPrefix+"AiFormatConfigGet"))
}
// 【需要token】中间件验证的路由
backend.Use(authorization.CheckTokenAuth())
{

@ -0,0 +1,24 @@
{
"blockquote_style": "side-quote",
"body_style": "normal-text",
"codeblock_style": [
"fancy-code",
"fancy-code-dark"
],
"heading_style": [
[
"h3",
"heading1"
],
[
"h4",
"heading2"
],
[
"h5",
"heading3"
]
],
"list_style": "disc",
"title_style": "title1"
}

@ -0,0 +1,26 @@
[
{
"element_name": "p",
"style_classes": [
"a"
],
"style_content": ".ck-content p.a {\r\n color: green;\r\n}",
"style_name": "a"
},
{
"element_name": "p",
"style_classes": [
"aa"
],
"style_content": ".ck-content p.aa {\r\n color: green;\r\n}",
"style_name": "aa"
},
{
"element_name": "p",
"style_classes": [
"test"
],
"style_content": ".ck-content p.test {\r\n font-family: fantasy;\r\n color: orange;\r\n font-style: italic;\r\n text-shadow: #FC0 1px 0 10px;\r\n}",
"style_name": "test"
}
]

@ -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'],
//自定义设置字体

@ -1,87 +1,121 @@
// utils.js
import { MarkdownToHtml } from '@ckeditor/ckeditor5-markdown-gfm/src/markdown2html/markdown2html.js';
import { HtmlToMarkdown } from '@ckeditor/ckeditor5-markdown-gfm/src/html2markdown/html2markdown.js';
import { useStore } from 'vuex';
// 获取用户配置
export function getUserConfigFromBackend() {
// TODO 请求用户样式
const options = {};
// 字体、字号、样式
const {
fontFamilyOptions = [
'default',
'宋体',
'新宋体',
'仿宋',
'楷体',
'微软雅黑',
'黑体',
'华文仿宋',
'华文楷体',
'华文隶书',
'华文宋体',
'华文细黑',
'华文新魏',
'华文行楷',
'华文中宋',
'隶书',
'苹方 常规',
'幼圆',
'Times New Roman'
],
// 五号,小四,四号,小三,三号,小二,二号
fontSizeOptions = [14, 'default', 16, 18.6, 20, 21.3, 24, 29.3],
styleDefinitions = [
{
name: 'Article category',
element: 'h3',
classes: ['category']
},
{
name: 'Title',
element: 'h2',
classes: ['document-title']
},
{
name: 'Subtitle',
element: 'h3',
classes: ['document-subtitle']
},
{
name: 'Info box',
element: 'p',
classes: ['info-box']
},
{
name: 'Side quote',
element: 'blockquote',
classes: ['side-quote']
},
{
name: 'Marker',
element: 'span',
classes: ['marker']
},
{
name: 'Spoiler',
element: 'span',
classes: ['spoiler']
},
{
name: 'Code (dark)',
element: 'pre',
classes: ['fancy-code', 'fancy-code-dark']
},
{
name: 'Code (bright)',
element: 'pre',
classes: ['fancy-code', 'fancy-code-bright']
}
]
} = options;
// 如果传入的options没有对应项使用默认值
// Make the function async so we can use await
export async function getUserConfigFromBackend() {
const fontFamilyOptions = [
'default',
'宋体',
'新宋体',
'仿宋',
'楷体',
'微软雅黑',
'黑体',
'华文仿宋',
'华文楷体',
'华文隶书',
'华文宋体',
'华文细黑',
'华文新魏',
'华文行楷',
'华文中宋',
'隶书',
'苹方 常规',
'幼圆',
'Times New Roman'
];
const fontSizeOptions = [14, 'default', 16, 18.6, 20, 21.3, 24, 29.3];
let styleDefinitions = [
{
name: 'Article category',
element: 'h3',
classes: ['category']
},
{
name: 'Title',
element: 'h2',
classes: ['document-title']
},
{
name: 'Subtitle',
element: 'h3',
classes: ['document-subtitle']
},
{
name: 'Info box',
element: 'p',
classes: ['info-box']
},
{
name: 'Side quote',
element: 'blockquote',
classes: ['side-quote']
},
{
name: 'Marker',
element: 'span',
classes: ['marker']
},
{
name: 'Spoiler',
element: 'span',
classes: ['spoiler']
},
{
name: 'Code (dark)',
element: 'pre',
classes: ['fancy-code', 'fancy-code-dark']
},
{
name: 'Code (bright)',
element: 'pre',
classes: ['fancy-code', 'fancy-code-bright']
}
];
try {
const formData = new FormData();
formData.append('user_name', useStore().state.user.username);
const requestOptions = {
method: 'POST',
body: formData
};
console.log(requestOptions);
// 使用 await 等待请求完成
const response = await fetch("http://localhost:14514/admin/user_config/user_style_get", requestOptions);
if (!response.ok) {
throw new Error("请求出错");
}
const data = await response.json();
console.log("POST请求成功", data);
// 处理返回的样式数据
data.data.forEach(style => {
styleDefinitions.push({
name: style.style_name,
element: style.element_name,
classes: style.style_classes
});
// 将 style_content 动态插入到页面的 CSS 中
const styleElement = document.createElement('style');
styleElement.textContent = style.style_content;
document.head.appendChild(styleElement);
});
console.log("styleDefinitions: ", styleDefinitions);
} catch (error) {
console.error("POST请求出错", error);
}
console.log("styleDefinitions: ", styleDefinitions);
return {
fontFamily: {
options: fontFamilyOptions
@ -161,45 +195,112 @@ export function html2markdown(htmlString) {
}
// 请求用户AI生成的样式配置
export function getUserAILayoutConfig() {
// TODO 请求用户AI生成的样式配置
const options = {};
// 对应的样式定义在`generatedStyle.css`中
const {
// title
titleStyleOption = 'title1',
// 标题样式配置 如 一、二 12 1. 2. I. II. A. B. (一)、(二)
// 每级标题按顺序应用 [标题大小,标题样式] 大小也可自定义
headingStyleOption = [['h3', 'heading1'], ['h4', 'heading2'], ['h5', 'heading3']], // limit 3
// 列表样式配置
listStyleOption = 'square',
// 下面的各项样式每一个对应的均会应用
// 正文样式配置 小四宋体
bodyStyleOption = ['normal-text'],
// 块引用样式配置
blockquoteStyleOption = ['side-quote'],
// 代码块样式配置
codeBlockStyleOption = ['fancy-code', 'fancy-code-bright'],
} = options;
// 如果传入的options没有对应项使用默认值
export async function getUserAILayoutConfig() {
// 设置默认值
let options = {
titleStyleOption: 'title1',
headingStyleOption: [['h3', 'heading1'], ['h4', 'heading2'], ['h5', 'heading3']],
listStyleOption: 'square',
bodyStyleOption: ['normal-text'],
blockquoteStyleOption: ['side-quote'],
codeBlockStyleOption: ['fancy-code', 'fancy-code-bright']
};
try {
const formData = new FormData();
formData.append('user_name', window.username);
const requestOptions = {
method: 'POST',
body: formData
};
console.log(requestOptions);
// 使用 await 等待请求完成
const response = await fetch("http://localhost:14514/admin/user_config/ai_format_config_get", requestOptions);
if (!response.ok) {
throw new Error("请求出错");
}
const data = await response.json();
console.log("POST请求成功", data);
// 根据后端返回的数据更新配置
if (data.title_style) {
options.titleStyleOption = data.title_style;
}
if (data.heading_style) {
options.headingStyleOption = data.heading_style;
}
if (data.list_style) {
options.listStyleOption = data.list_style;
}
if (data.body_style) {
options.bodyStyleOption = [data.body_style];
}
if (data.blockquote_style) {
options.blockquoteStyleOption = [data.blockquote_style];
}
if (data.codeblock_style) {
options.codeBlockStyleOption = data.codeblock_style;
}
} catch (error) {
console.error("POST请求出错", error);
// 发生错误时使用默认配置
}
// 返回配置对象
console.log('getUserAILayoutConfig', options);
return {
titleStyle: {
option: titleStyleOption
option: options.titleStyleOption
},
headingStyle: {
option: headingStyleOption
option: options.headingStyleOption
},
bodyStyle: {
option: bodyStyleOption
option: options.bodyStyleOption
},
blockquoteStyle: {
option: blockquoteStyleOption
option: options.blockquoteStyleOption
},
codeBlockStyle: {
option: codeBlockStyleOption
option: options.codeBlockStyleOption
},
listStyle: {
option: listStyleOption
option: options.listStyleOption
}
};
}
export async function saveUserAILayoutConfig(config) {
console.log('saveUserAILayoutConfig', config);
console.log('window.store', window.username);
try {
const formData = new FormData();
formData.append('user_name', window.username);
formData.append('title_style', config.titleStyle.option);
formData.append('heading_style', config.headingStyle.option);
formData.append('list_style', config.listStyle.option);
formData.append('body_style', config.bodyStyle.option);
formData.append('blockquote_style', config.blockquoteStyle.option);
formData.append('codeblock_style', config.codeBlockStyle.option);
const requestOptions = {
method: 'POST',
body: formData
};
console.log(requestOptions);
// 使用 await 等待请求完成
const response = await fetch("http://localhost:14514/admin/user_config/ai_format_config_save", requestOptions);
if (!response.ok) {
throw new Error("请求出错");
}
const data = await response.json();
console.log("POST请求成功", data);
} catch (error) {
console.error("POST请求出错", error);
}
}

@ -6,6 +6,16 @@
font-size: 29.3px;
font-weight: 'bold';
}
.ck-content .title2{
font-family: '黑体';
font-size: 24.4px;
font-weight: 'bold';
}
.ck-content .title3{
font-family: '宋体';
font-size: 20.3px;
font-weight: 'bold';
}
/* heading style */
/* 使用前必须在对应元素的父元素中将对应计数器重置 */
.ck-content .heading1::before{
@ -202,4 +212,29 @@
text-indent: 2em;
margin: 0;
padding: 0;
}
/* 四号宋体 */
.ck-content p.songti-text {
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-content p.kaiti-text {
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;
}

@ -16,7 +16,7 @@
</div>
<el-button type="primary" @click="toggleSidebar()" style="display:none"
id="toggleSidebarButton">打开/关闭侧边栏</el-button>
<div class="sidebar" :class="{ 'active': isSidebarOpen }" >
<div class="sidebar" :class="{ 'active': isSidebarOpen }">
<!-- 侧边栏内容 -->
<el-menu class="sidebar-menu" :class="{ 'active': isNavbarOpen }" :collapse="isNavbarOpen">
<el-sub-menu index="1" class="horizontal-sub-menu">
@ -43,7 +43,14 @@
<!-- Dynamic content based on navigation selection -->
<div v-if="currentContent === 'refine_doc'" id='refine_doc_block'>
<div class="refine_docs" id="refine_docs">
<el-button style="margin-left: 10px; width: 40px; height: 40px;" @click="clearRefineDOCContent()"><svg t="1731509926355" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8154" width="20" height="20"><path d="M1011.43552 981.92384l-68.4032-394.40384h23.10144c18.5856 0 33.54624-14.97088 33.54624-33.55648V306.16576c0-18.5856-14.97088-33.55648-33.54624-33.55648H648.6528V37.71392c0-18.5856-14.97088-33.55648-33.55648-33.55648H408.59648c-18.5856 0-33.55648 14.97088-33.55648 33.55648v234.88512H57.5488c-18.5856 0-33.54624 14.97088-33.54624 33.55648v247.79776c0 18.5856 14.97088 33.55648 33.54624 33.55648h23.10144L12.24704 981.9136c-0.38912 1.9456-0.512 3.87072-0.512 5.6832 0 18.5856 14.97088 33.54624 33.55648 33.54624h933.10976c1.93536 0 3.88096-0.12288 5.6832-0.512 18.31936-3.08224 30.57664-20.51072 27.35104-38.7072zM114.33984 362.94656h351.03744V94.50496h92.928v268.4416h351.03744v134.22592H114.33984V362.94656zM718.336 930.816V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H387.9424V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H112.78336l58.20416-335.55456h681.5744L910.76608 930.816H718.336z m0 0" fill="#2C2C2C" p-id="8155"></path></svg></el-button>
<el-button style="margin-left: 10px; width: 40px; height: 40px;"
@click="clearRefineDOCContent()"><svg t="1731509926355" class="icon"
viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8154"
width="20" height="20">
<path
d="M1011.43552 981.92384l-68.4032-394.40384h23.10144c18.5856 0 33.54624-14.97088 33.54624-33.55648V306.16576c0-18.5856-14.97088-33.55648-33.54624-33.55648H648.6528V37.71392c0-18.5856-14.97088-33.55648-33.55648-33.55648H408.59648c-18.5856 0-33.55648 14.97088-33.55648 33.55648v234.88512H57.5488c-18.5856 0-33.54624 14.97088-33.54624 33.55648v247.79776c0 18.5856 14.97088 33.55648 33.54624 33.55648h23.10144L12.24704 981.9136c-0.38912 1.9456-0.512 3.87072-0.512 5.6832 0 18.5856 14.97088 33.54624 33.55648 33.54624h933.10976c1.93536 0 3.88096-0.12288 5.6832-0.512 18.31936-3.08224 30.57664-20.51072 27.35104-38.7072zM114.33984 362.94656h351.03744V94.50496h92.928v268.4416h351.03744v134.22592H114.33984V362.94656zM718.336 930.816V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H387.9424V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H112.78336l58.20416-335.55456h681.5744L910.76608 930.816H718.336z m0 0"
fill="#2C2C2C" p-id="8155"></path>
</svg></el-button>
<div v-for="(item, index) in currentRefineDocContent" :key="index" class="messages">
<div class="message">
<p>{{ item.oldContent }}</p>
@ -51,17 +58,24 @@
<div class="message-answer">
<pre>{{ item.newContent }}</pre>
</div>
</div>
</div>
</div>
</div>
<div v-if="currentContent === 'ai_recg'">
<div class="ai_recgs" id="ai_recgs">
<el-button style="margin-left: 10px; width: 40px; height: 40px;" @click="clearAiRecgContent()"><svg t="1731509926355" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8154" width="20" height="20"><path d="M1011.43552 981.92384l-68.4032-394.40384h23.10144c18.5856 0 33.54624-14.97088 33.54624-33.55648V306.16576c0-18.5856-14.97088-33.55648-33.54624-33.55648H648.6528V37.71392c0-18.5856-14.97088-33.55648-33.55648-33.55648H408.59648c-18.5856 0-33.55648 14.97088-33.55648 33.55648v234.88512H57.5488c-18.5856 0-33.54624 14.97088-33.54624 33.55648v247.79776c0 18.5856 14.97088 33.55648 33.54624 33.55648h23.10144L12.24704 981.9136c-0.38912 1.9456-0.512 3.87072-0.512 5.6832 0 18.5856 14.97088 33.54624 33.55648 33.54624h933.10976c1.93536 0 3.88096-0.12288 5.6832-0.512 18.31936-3.08224 30.57664-20.51072 27.35104-38.7072zM114.33984 362.94656h351.03744V94.50496h92.928v268.4416h351.03744v134.22592H114.33984V362.94656zM718.336 930.816V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H387.9424V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H112.78336l58.20416-335.55456h681.5744L910.76608 930.816H718.336z m0 0" fill="#2C2C2C" p-id="8155"></path></svg></el-button>
<el-button style="margin-left: 10px; width: 40px; height: 40px;"
@click="clearAiRecgContent()"><svg t="1731509926355" class="icon"
viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8154"
width="20" height="20">
<path
d="M1011.43552 981.92384l-68.4032-394.40384h23.10144c18.5856 0 33.54624-14.97088 33.54624-33.55648V306.16576c0-18.5856-14.97088-33.55648-33.54624-33.55648H648.6528V37.71392c0-18.5856-14.97088-33.55648-33.55648-33.55648H408.59648c-18.5856 0-33.55648 14.97088-33.55648 33.55648v234.88512H57.5488c-18.5856 0-33.54624 14.97088-33.54624 33.55648v247.79776c0 18.5856 14.97088 33.55648 33.54624 33.55648h23.10144L12.24704 981.9136c-0.38912 1.9456-0.512 3.87072-0.512 5.6832 0 18.5856 14.97088 33.54624 33.55648 33.54624h933.10976c1.93536 0 3.88096-0.12288 5.6832-0.512 18.31936-3.08224 30.57664-20.51072 27.35104-38.7072zM114.33984 362.94656h351.03744V94.50496h92.928v268.4416h351.03744v134.22592H114.33984V362.94656zM718.336 930.816V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H387.9424V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H112.78336l58.20416-335.55456h681.5744L910.76608 930.816H718.336z m0 0"
fill="#2C2C2C" p-id="8155"></path>
</svg></el-button>
<div v-for="(item, index) in currentAiRecgContent" :key="index" class="messages">
<div class="message-answer">
<pre>{{ item.newContent }}</pre>
</div>
</div>
</div>
</div>
</div>
<div v-if="currentContent === 'manual'">
@ -360,6 +374,7 @@ export default {
editor.setData(pageContent);
// editorwindow便使|
window.editor = editor;
window.username = this.store.state.user.username;
// CSS DFZ
},
// sidebar
@ -373,30 +388,30 @@ export default {
this.currentContent = formType;
},
//
showRefineDOCContent(tag){
showRefineDOCContent(tag) {
this.currentContent = 'refine_doc';
this.store.commit('setCurrentTag', tag);
if (this.store.getters.getCurrentContent){
this.currentRefineDocContent = this.store.getters.getCurrentContent.reduce((acc, current) => [current,...acc], []);
if (this.store.getters.getCurrentContent) {
this.currentRefineDocContent = this.store.getters.getCurrentContent.reduce((acc, current) => [current, ...acc], []);
}
},
showAiRecgContent(tag){
showAiRecgContent(tag) {
this.currentContent = 'ai_recg';
this.store.commit('setCurrentTag', tag);
if (this.store.getters.getCurrentContent){
this.currentAiRecgContent = this.store.getters.getCurrentContent.reduce((acc, current) => [current,...acc], []);
if (this.store.getters.getCurrentContent) {
this.currentAiRecgContent = this.store.getters.getCurrentContent.reduce((acc, current) => [current, ...acc], []);
}
},
clearRefineDOCContent(){
clearRefineDOCContent() {
this.store.commit('clearContentsForTag', this.store.getters.getCurrentTag);
if (this.store.getters.getCurrentContent){
this.currentRefineDocContent = this.store.getters.getCurrentContent.reduce((acc, current)=> [current,...acc], []);
if (this.store.getters.getCurrentContent) {
this.currentRefineDocContent = this.store.getters.getCurrentContent.reduce((acc, current) => [current, ...acc], []);
}
},
clearAiRecgContent(){
clearAiRecgContent() {
this.store.commit('clearContentsForTag', this.store.getters.getCurrentTag);
if (this.store.getters.getCurrentContent){
this.currentAiRecgContent = this.store.getters.getCurrentContent.reduce((acc, current)=> [current,...acc], []);
if (this.store.getters.getCurrentContent) {
this.currentAiRecgContent = this.store.getters.getCurrentContent.reduce((acc, current) => [current, ...acc], []);
}
},
//
@ -420,7 +435,35 @@ export default {
console.log(cssClass); // cssClass
alert(cssClass);
const formData = new FormData();
formData.append('user_name', this.store.state.user.username);
formData.append('style_name', className);
formData.append('element_name', 'p');
formData.append('style_classes', [className]);
formData.append('style_content', cssClass);
const requestOptions = {
method: 'POST',
body: formData
};
console.log(requestOptions);
fetch("http://localhost:14514/admin/user_config/user_style_save", requestOptions)
.then(response => {
if (!response.ok) {
throw new Error("请求出错");
}
// JSON使response.json()
return response.json();
})
.then(data => {
console.log("POST请求成功", data);
alert("保存成功!");
})
.catch(error => {
console.error("POST请求出错", error);
});
} else {
alert("类名不能为空!");
}
@ -482,7 +525,7 @@ export default {
// APIresponse
try {
const response = await fetch('/web_api/admin/ai_layout/style_generate', {
const response = await fetch('http://localhost:14514/admin/ai_layout/style_generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -603,14 +646,33 @@ export default {
// 使style
// styleDefinition:[name: 'styleName',element: 'element',classes: [className]]
const styleName = prompt("请输入样式名称:");
// styleDefiniton
const styleDefinition = {
name: styleName,
element: previewElement.tagName,
classes: classNames
const formData = new FormData();
formData.append('user_name', this.store.state.user.username);
formData.append('style_name', styleName);
formData.append('element_name', previewElement.tagName);
formData.append('style_classes', classNames);
formData.append('style_content', cssText);
const requestOptions = {
method: 'POST',
body: formData
};
console.log(styleDefinition);
console.log(cssText);// css
console.log(requestOptions);
fetch("http://localhost:14514/admin/user_config/user_style_save", requestOptions)
.then(response => {
if (!response.ok) {
throw new Error("请求出错");
}
// JSON使response.json()
return response.json();
})
.then(data => {
console.log("POST请求成功", data);
alert("保存成功!");
})
.catch(error => {
console.error("POST请求出错", error);
});
};
//
buttonsMessageDiv.appendChild(saveButton);
@ -659,22 +721,22 @@ export default {
return clearButton;
}
},
mounted() {
this.config = setConfig();
this.isLayoutReady = true;
emitter.on('show-refine-doc-sidebar', (id) => {
this.isSidebarOpen = true;
this.isNavbarOpen = true;
this.showRefineDOCContent(id);
console.log(this.currentContent)
});
emitter.on('show-ai-recg-sidebar', (id) => {
this.isSidebarOpen = true;
this.isNavbarOpen = true;
this.showAiRecgContent(id);
console.log(this.currentContent)
});
},
async mounted() {
this.config = await setConfig();
this.isLayoutReady = true;
emitter.on('show-refine-doc-sidebar', (id) => {
this.isSidebarOpen = true;
this.isNavbarOpen = true;
this.showRefineDOCContent(id);
console.log(this.currentContent)
});
emitter.on('show-ai-recg-sidebar', (id) => {
this.isSidebarOpen = true;
this.isNavbarOpen = true;
this.showAiRecgContent(id);
console.log(this.currentContent)
});
},
components: {
//
ElButton, ElInput, ElSelect, ElOption, ElForm, ElFormItem, ElMenu, ElMenuItem, ElColorPicker, ElSubMenu

Loading…
Cancel
Save