Compare commits

...

21 Commits

Author SHA1 Message Date
joefalmko c6c182a7de Merge branch 'master' of code.gitlink.org.cn:zolo2468216537/coeditor
2 months ago
joefalmko fed8981ebb 增加保存和获取用户自定义样式、保存和获取用户自动排版设置
2 months ago
dfz fb9975942c 调整路由信息
2 months ago
dfz 56f8b65c39 添加前端文件保存到后端的内容
2 months ago
dfz 773cc03988 添加文件前端文件保存到后端的内容
2 months ago
dfz 7e59848958 添加文件夹操作相关内容
2 months ago
dfz e86d34ef00 添加文件操作相关内容
2 months ago
dfz ac9ba2f056 添加文件修改内容
2 months ago
dfz 80b405d64c 删除错误提交
2 months ago
dfz ec37768f0b 添加文件操作相关接口
2 months ago
dfz 1ce989b3df 增加文件操作接口
2 months ago
dfz 1b985f07cb 增加文件操作接口
2 months ago
dfz 106461d7f6 增加文件操作接口
2 months ago
dfz 4025e35341 添加文件操作相关路由
2 months ago
dfz 0fec9b88b9 增加文件操作相关接口
2 months ago
joefalmko baa9131710 Merge branch 'master' of code.gitlink.org.cn:zolo2468216537/coeditor
2 months ago
joefalmko f81bd3b14a nothing
2 months ago
linlnf 8ff0dee801 增加jpeg 格式图片识别的支持
2 months ago
lc c466c828b0 fix register
2 months ago
joefalmko 022d7e16c3 完善ai排版,可以将文件内容使用大模型进行初步排版后,应用用户定义的样式规则,如代码块、块引用、文本样式等,可以设置每级标题的样式如(一) 一、 I. 等并自动计数,不会改变原有的图片样式,仅对文本进行处理,不会修改原有文本的内容。
2 months ago
linlnf 8f29601051 前端增加图片识别功能,阉掉语音识别功能
3 months ago

@ -239,4 +239,183 @@ type|form-data|string|必填|"summary"/"decoration"/"correction"/"extension"/"tr
},
"msg": "Success"
}
```
```
#### 创建文件
> <font color=#FF4500>*post*/admin/file/file_creat</font>
参数字段|参数属性|类型|选项
---|---|---|---|---
user_name|form-data|string|必填
file_name|form-data|string|必填
file_path|form-data|string|必填
> 返回示例:
```json
{
{
"code": 200,
"data": "文件创建成功",
"msg": "Success"
}
}
```
#### 创建文件夹
> <font color=#FF4500>*post*/admin/file/folder_creat</font>
参数字段|参数属性|类型|选项
---|---|---|---|---
user_name|form-data|string|必填
folder_name|form-data|string|必填
folder_path|form-data|string|必填
> 返回示例:
```json
{
{
"code": 200,
"data": "文件夹创建成功",
"msg": "Success"
}
}
```
#### 打开文件
> <font color=#FF4500>*post*/admin/file/file_get</font>
参数字段|参数属性|类型|选项
---|---|---|---|---
user_name|form-data|string|必填
file_name|form-data|string|必填
file_path|form-data|string|必填
> 返回示例:
```json
{
{
"code": 200,
"data": "5Z+65LqO5aSn5bCP5qih5Z6L5Y2P5ZCM55qE5Zyo57q/57yW6L6R5Zmo",
"msg": "Success - 文件获取成功"
}
}
```
#### 文件重命名
> <font color=#FF4500>*post*/admin/file/file_rename</font>
参数字段|参数属性|类型|选项
---|---|---|---|---
user_name|form-data|string|必填
file_name|form-data|string|必填
file_path|form-data|string|必填
newname|form-data|string|必填
> 返回示例:
```json
{
{
"code": 200,
"data": "文件重命名成功",
"msg": "Success"
}
}
```
#### 移动文件
> <font color=#FF4500>*post*/admin/file/file_move</font>
参数字段|参数属性|类型|选项
---|---|---|---|---
user_name|form-data|string|必填
file_name|form-data|string|必填
file_path|form-data|string|必填
newpath|form-data|string|必填
> 返回示例:
```json
{
{
"code": 200,
"data": "文件移动成功",
"msg": "Success"
}
}
```
#### 保存文件
> <font color=#FF4500>*post*/admin/file/file_save</font>
参数字段|参数属性|类型|选项
---|---|---|---|---
user_name|form-data|string|必填
file_name|form-data|string|必填
save_path|form-data|string|必填
file_data|form-data|string|必填
> 返回示例:
```json
{
{
"code": 200,
"data": "文件保存成功",
"msg": "Success"
}
}
```
#### 删除文件
> <font color=#FF4500>*post*/admin/file/file_delete </font>
参数字段|参数属性|类型|选项
---|---|---|---|---
user_name|form-data|string|必填
file_name|form-data|string|必填
file_path|form-data|string|必填
> 返回示例:
```json
{
{
"code": 200,
"data": "文件删除成功",
"msg": "Success"
}
}
```
#### 打开文件夹
> <font color=#FF4500>*post*/admin/file/folder_get </font>
参数字段|参数属性|类型|选项
---|---|---|---|---
user_name|form-data|string|必填
folder_path|form-data|string|必填
> 返回示例:
```json
{
"code": 200,
"data": [
{
"name": "001",
"isDir": false,
"path": "001"
},
{
"name": "002",
"isDir": false,
"path": "002"
},
{
"name": "003",
"isDir": true,
"path": "003"
},
{
"name": "222",
"isDir": true,
"path": "222"
},
{
"name": "test",
"isDir": false,
"path": "test"
}
],
"msg": "Success - 文件夹内容获取成功"
}
```

@ -54,6 +54,18 @@ const (
CurdLogoutFailMsg string = "登出失败"
CurdPublicKeyFailCode int = -400209
CurdPublicKeyFailMsg string = "密钥获取失败"
CurdFilePathErrorCode int = -400210
CurdFilePathErrorMsg string = "文件路径错误"
CurdFileNotExistCode int = -400211
CurdFileNotExistMsg string = "文件不存在"
CurdFileUploadErrorCode int = -400212
CurdFileUploadErrorMsg string = "文件上传失败"
CurdFileTypeErrorCode int = -400213
CurdFileTypeErrorMsg string = "文件类型错误"
CurdFolderPathErrorCode int = -400214
CurdFolderPathErrorMsg string = "文件夹路径错误"
CurdFolderNotExistCode int = -400215
CurdFolderNotExistMsg string = "文件夹不存在"
//文件上传
FilesUploadFailCode int = -400250
FilesUploadFailMsg string = "文件上传失败, 获取上传文件发生错误!"

@ -0,0 +1,231 @@
package web
import (
"fmt"
"goskeleton/app/global/consts"
"goskeleton/app/service/file"
"goskeleton/app/utils/response"
"os"
"github.com/gin-gonic/gin"
)
type File struct {
}
// 创建文件
func (f *File) Creat(context *gin.Context) {
//从context获得路径和文件名并拼接获得最终文件路径
user_name := context.GetString(consts.ValidatorPrefix + "user_name")
file_Path := context.GetString(consts.ValidatorPrefix + "file_path")
file_Name := context.GetString(consts.ValidatorPrefix + "file_name")
final_Path := fmt.Sprintf("%s/%s", file_Path, file_Name)
//file, err := os.Create(final_Path)
if err := file.CreateFile(user_name, final_Path); err != nil {
response.Fail(context, consts.CurdCreatFailCode, consts.CurdCreatFailMsg, err.Error())
return
}
// 文件创建成功,返回成功响应给客户端
response.Success(context, consts.CurdStatusOkMsg, "文件创建成功")
}
// 创建文件夹
func (f *File) FolderCreat(context *gin.Context) {
//从context获得路径和文件夹名并拼接获得最终文件夹路径
user_name := context.GetString(consts.ValidatorPrefix + "user_name")
folder_Path := context.GetString(consts.ValidatorPrefix + "folder_path")
folder_Name := context.GetString(consts.ValidatorPrefix + "folder_name")
final_Path := fmt.Sprintf("%s/%s", folder_Path, folder_Name)
if err := file.CreateFolder(user_name, final_Path); err != nil {
response.Fail(context, consts.CurdCreatFailCode, consts.CurdCreatFailMsg, err.Error())
return
}
// 文件创建成功,返回成功响应给客户端
response.Success(context, consts.CurdStatusOkMsg, "文件夹创建成功")
}
// 打开文件
func (f *File) Get(context *gin.Context) {
//从context获得路径和文件名并拼接获得最终文件路径
user_name := context.GetString(consts.ValidatorPrefix + "user_name")
file_Path := context.GetString(consts.ValidatorPrefix + "file_path")
file_Name := context.GetString(consts.ValidatorPrefix + "file_name")
final_Path := fmt.Sprintf("%s/%s", file_Path, file_Name)
fileContentBase64, err := file.GetFile(user_name, final_Path)
if err != nil {
// 根据不同的错误类型,返回更具体、合适的错误响应给客户端
switch err := err.(type) {
case *os.PathError:
//文件路径错误
response.Fail(context, consts.CurdFilePathErrorCode, consts.CurdFilePathErrorMsg, err.Error())
default:
if err == os.ErrNotExist {
//文件不存在错误
response.Fail(context, consts.CurdFileNotExistCode, consts.CurdFileNotExistMsg, err.Error())
}
}
return
}
// 文件获取成功,返回成功响应给客户端
response.Success(context, consts.CurdStatusOkMsg+" - "+"文件获取成功", fileContentBase64)
}
// 文件重命名
func (f *File) Rename(context *gin.Context) {
//从context获得路径和文件名并拼接获得最终文件路径
user_name := context.GetString(consts.ValidatorPrefix + "user_name")
file_Path := context.GetString(consts.ValidatorPrefix + "file_path")
file_Name := context.GetString(consts.ValidatorPrefix + "file_name")
final_Path := fmt.Sprintf("%s/%s", file_Path, file_Name)
//old_Name := context.GetString(consts.ValidatorPrefix + "oldname")
new_Name := context.GetString(consts.ValidatorPrefix + "newname")
new_finalpath := fmt.Sprintf("%s/%s", file_Path, new_Name)
err := file.FileRename(user_name, final_Path, new_finalpath)
if err != nil {
// 根据不同的错误类型,返回更具体、合适的错误响应给客户端
switch err := err.(type) {
case *os.PathError:
//文件路径错误
response.Fail(context, consts.CurdFilePathErrorCode, consts.CurdFilePathErrorMsg, err.Error())
default:
if err == os.ErrNotExist {
//文件不存在错误
response.Fail(context, consts.CurdFileNotExistCode, consts.CurdFileNotExistMsg, err.Error())
}
}
return
}
// 文件重命名成功,返回成功响应给客户端
response.Success(context, consts.CurdStatusOkMsg, "文件重命名成功")
}
// 文件移动
func (f *File) Move(context *gin.Context) {
//从context获得路径和文件名并拼接获得最终文件路径
user_name := context.GetString(consts.ValidatorPrefix + "user_name")
file_Path := context.GetString(consts.ValidatorPrefix + "file_path")
file_Name := context.GetString(consts.ValidatorPrefix + "file_name")
final_Path := fmt.Sprintf("%s/%s", file_Path, file_Name)
//old_Name := context.GetString(consts.ValidatorPrefix + "oldname")
new_Path := context.GetString(consts.ValidatorPrefix + "newpath")
new_finalpath := fmt.Sprintf("%s/%s", new_Path, file_Name)
err := file.FileMove(user_name, final_Path, new_finalpath)
if err != nil {
// 根据不同的错误类型,返回更具体、合适的错误响应给客户端
switch err := err.(type) {
case *os.PathError:
//文件路径错误
response.Fail(context, consts.CurdFilePathErrorCode, consts.CurdFilePathErrorMsg, err.Error())
default:
if err == os.ErrNotExist {
//文件不存在错误
response.Fail(context, consts.CurdFileNotExistCode, consts.CurdFileNotExistMsg, err.Error())
}
}
return
}
// 文件移动成功,返回成功响应给客户端
response.Success(context, consts.CurdStatusOkMsg, "文件移动成功")
}
// 文件保存
func (f *File) Save(context *gin.Context) {
//从context获得路径和文件名并拼接获得最终文件路径
user_name := context.GetString(consts.ValidatorPrefix + "user_name")
Save_path := context.GetString(consts.ValidatorPrefix + "save_path")
file_Name := context.GetString(consts.ValidatorPrefix + "file_name")
final_savePath := fmt.Sprintf("%s/%s", Save_path, file_Name)
fileDataBase64 := context.PostForm("file_data")
if fileDataBase64 == "" {
response.Fail(context, consts.CurdFileNotExistCode, consts.CurdFileNotExistMsg, nil)
return
}
err := file.FileSave(user_name, final_savePath, fileDataBase64)
if err != nil {
// 根据不同的错误类型,返回更具体、合适的错误响应给客户端
switch err := err.(type) {
case *os.PathError:
//文件路径错误
response.Fail(context, consts.CurdFilePathErrorCode, consts.CurdFilePathErrorMsg, err.Error())
default:
if err == os.ErrNotExist {
//文件不存在错误
response.Fail(context, consts.CurdFileNotExistCode, consts.CurdFileNotExistMsg, err.Error())
}
}
return
}
// 文件保存成功,返回成功响应给客户端
response.Success(context, consts.CurdStatusOkMsg, "文件保存成功")
}
// 文件删除
func (f *File) Delete(context *gin.Context) {
// 从context获得用户名、文件路径和文件名并拼接获得最终文件路径
user_name := context.GetString(consts.ValidatorPrefix + "user_name")
file_Path := context.GetString(consts.ValidatorPrefix + "file_path")
file_Name := context.GetString(consts.ValidatorPrefix + "file_name")
final_Path := fmt.Sprintf("%s/%s", file_Path, file_Name)
err := file.FileDe(user_name, final_Path)
if err != nil {
// 根据不同的错误类型,返回更具体、合适的错误响应给客户端
switch err := err.(type) {
case *os.PathError:
//文件路径错误
response.Fail(context, consts.CurdFilePathErrorCode, consts.CurdFilePathErrorMsg, err.Error())
default:
if err == os.ErrNotExist {
//文件不存在错误
response.Fail(context, consts.CurdFileNotExistCode, consts.CurdFileNotExistMsg, err.Error())
}
}
return
}
// 文件删除成功,返回成功响应给客户端
response.Success(context, consts.CurdStatusOkMsg, "文件删除成功")
}
// 打开文件夹
func (f *File) FolderGet(context *gin.Context) {
user_name := context.GetString(consts.ValidatorPrefix + "user_name")
folder_path := context.GetString(consts.ValidatorPrefix + "folder_path")
folderContents, err := file.GetFolder(user_name, folder_path)
if err != nil {
// 根据不同的错误类型,返回相应的错误响应给客户端
switch err := err.(type) {
case *os.PathError:
response.Fail(context, consts.CurdFolderPathErrorCode, consts.CurdFolderPathErrorMsg, err.Error())
default:
if err == os.ErrNotExist {
response.Fail(context, consts.CurdFolderNotExistCode, consts.CurdFolderNotExistMsg, err.Error())
}
}
return
}
// 文件夹获取成功,返回成功响应给客户端,携带文件夹内容信息
response.Success(context, consts.CurdStatusOkMsg+" - "+"文件夹内容获取成功", folderContents)
}

@ -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()
}

@ -6,9 +6,11 @@ import (
"goskeleton/app/http/validator/common/upload_files"
"goskeleton/app/http/validator/common/websocket"
"goskeleton/app/http/validator/web/ai_doc.go"
"goskeleton/app/http/validator/web/ai_layout"
"goskeleton/app/http/validator/web/ai_recognition"
"goskeleton/app/http/validator/web/file"
"goskeleton/app/http/validator/web/users"
"goskeleton/app/http/validator/web/ai_layout"
"goskeleton/app/http/validator/web/editor"
)
// 各个业务模块验证器必须进行注册(初始化),程序启动时会自动加载到容器
@ -63,5 +65,46 @@ func WebRegisterValidator() {
containers.Set(key, ai_layout.StyleGenerate{})
key = consts.ValidatorPrefix + "LayoutGenerate"
containers.Set(key, ai_layout.LayoutGenerate{})
//文件相关操作
//创建文件
key = consts.ValidatorPrefix + "FileCreat"
containers.Set(key, file.FileCreat{})
//创建文件夹
key = consts.ValidatorPrefix + "FolderCreat"
containers.Set(key, file.FolderCreat{})
//打开文件
key = consts.ValidatorPrefix + "FileGet"
containers.Set(key, file.FileGet{})
//文件重命名
key = consts.ValidatorPrefix + "FileRename"
containers.Set(key, file.FileRename{})
//移动文件
key = consts.ValidatorPrefix + "FileMove"
containers.Set(key, file.FileMove{})
//保存文件
key = consts.ValidatorPrefix + "FileSave"
containers.Set(key, file.FileSave{})
//删除文件
key = consts.ValidatorPrefix + "FileDelete"
containers.Set(key, file.FileDelete{})
//打开文件夹
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{})
}

@ -8,8 +8,8 @@ import (
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"image"
_ "image/jpeg"
"math"
"github.com/gin-gonic/gin"
)
@ -50,6 +50,7 @@ func (p PicRecognition) CheckParams(context *gin.Context) {
// 将字节数据转换为图像以检查格式和尺寸
img, _, err := image.Decode(bytes.NewReader(decodedData))
if err != nil {
response.ErrorSystem(context, "PicRecognition表单参数验证器图片格式不正确", "")
return
}
bounds := img.Bounds()

@ -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)
}
}

@ -0,0 +1,37 @@
package file
import (
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type FileDelete struct {
// 表单参数验证结构体支持匿名结构体嵌套
BaseField
FileInfo
}
// 验证器语法,参见 Register.go文件有详细说明
func (f FileDelete) CheckParams(context *gin.Context) {
//1.基本的验证规则没有通过
if err := context.ShouldBind(&f); err != nil {
response.ValidatorError(context, err)
return
}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(f, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "FileDelete表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.File{}).Delete(extraAddBindDataContext)
}
}

@ -0,0 +1,37 @@
package file
import (
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type FileGet struct {
// 表单参数验证结构体支持匿名结构体嵌套
BaseField
FileInfo
}
// 验证器语法,参见 Register.go文件有详细说明
func (f FileGet) CheckParams(context *gin.Context) {
//1.基本的验证规则没有通过
if err := context.ShouldBind(&f); err != nil {
response.ValidatorError(context, err)
return
}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(f, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "FileGet表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.File{}).Get(extraAddBindDataContext)
}
}

@ -0,0 +1,38 @@
package file
import (
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type FileRename struct {
// 表单参数验证结构体支持匿名结构体嵌套
BaseField
FileInfo
Filename
}
// 验证器语法,参见 Register.go文件有详细说明
func (f FileRename) CheckParams(context *gin.Context) {
//1.基本的验证规则没有通过
if err := context.ShouldBind(&f); err != nil {
response.ValidatorError(context, err)
return
}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(f, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "FileRename表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.File{}).Rename(extraAddBindDataContext)
}
}

@ -0,0 +1,50 @@
package file
import (
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type FileSave struct {
// 表单参数验证结构体支持匿名结构体嵌套
BaseField
FileUpload
}
// 验证器语法,参见 Register.go文件有详细说明
func (f FileSave) CheckParams(context *gin.Context) {
//1.基本的验证规则没有通过
if err := context.ShouldBind(&f); err != nil {
response.ValidatorError(context, err)
return
}
// 尝试获取上传的文件,检查文件是否存在
//file, header, err := context.Request.FormFile("file")
//if err != nil {
// response.Fail(context, consts.CurdFileUploadErrorCode, consts.CurdFileUploadErrorMsg, "文件不存在或获取文件出错")
// return
//}
//defer file.Close()
// 对文件名后缀进行额外校验,确保是.docx文件假设只保存该类型文件
//if filepath.Ext(header.Filename) != ".docx" {
// response.Fail(context, consts.CurdFileTypeErrorCode, consts.CurdFileTypeErrorMsg, "只支持上传.docx文件")
// return
//}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(f, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "FileSave表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.File{}).Save(extraAddBindDataContext)
}
}

@ -0,0 +1,37 @@
package file
import (
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type FileCreat struct {
// 表单参数验证结构体支持匿名结构体嵌套
BaseField
FileInfo
}
// 验证器语法,参见 Register.go文件有详细说明
func (f FileCreat) CheckParams(context *gin.Context) {
//1.基本的验证规则没有通过
if err := context.ShouldBind(&f); err != nil {
response.ValidatorError(context, err)
return
}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(f, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "fileCreat表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.File{}).Creat(extraAddBindDataContext)
}
}

@ -0,0 +1,38 @@
package file
import (
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type FileMove struct {
// 表单参数验证结构体支持匿名结构体嵌套
BaseField
FileInfo
Filepath
}
// 验证器语法,参见 Register.go文件有详细说明
func (f FileMove) CheckParams(context *gin.Context) {
//1.基本的验证规则没有通过
if err := context.ShouldBind(&f); err != nil {
response.ValidatorError(context, err)
return
}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(f, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "FileMove表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.File{}).Move(extraAddBindDataContext)
}
}

@ -0,0 +1,37 @@
package file
import (
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type FolderCreat struct {
// 表单参数验证结构体支持匿名结构体嵌套
BaseField
Folder
}
// 验证器语法,参见 Register.go文件有详细说明
func (f FolderCreat) CheckParams(context *gin.Context) {
//1.基本的验证规则没有通过
if err := context.ShouldBind(&f); err != nil {
response.ValidatorError(context, err)
return
}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(f, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "folderCreat表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.File{}).FolderCreat(extraAddBindDataContext)
}
}

@ -0,0 +1,37 @@
package file
import (
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type FolderGet struct {
// 表单参数验证结构体支持匿名结构体嵌套
BaseField
FolderPath string `form:"folder_path" json:"folder_path" binding:"required"` // 文件夹路径必填
}
// 验证器语法,参见 Register.go文件有详细说明
func (f FolderGet) CheckParams(context *gin.Context) {
//1.基本的验证规则没有通过
if err := context.ShouldBind(&f); err != nil {
response.ValidatorError(context, err)
return
}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(f, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "FileGet表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.File{}).FolderGet(extraAddBindDataContext)
}
}

@ -0,0 +1,31 @@
package file
type BaseField struct {
UserName string `form:"user_name" json:"user_name" binding:"required,min=3"` // 必填、对于文本,表示它的长度>=1
}
type FileInfo struct {
FileName string `form:"file_name" json:"file_name" binding:"required"` // 文件名必填
Path string `form:"file_path" json:"file_path" binding:"required"` // 文件路径必填
}
type Folder struct {
FoldereName string `form:"folder_name" json:"folder_name" binding:"required"` // 文件夹名必填
FolderPath string `form:"folder_path" json:"folder_path" binding:"required"` // 文件夹路径必填
}
type Filename struct {
//O_FileName string `form:"oldname" json:"oldname" binding:"required"` // 旧文件名
N_FileName string `form:"newname" json:"newname" binding:"required"` // 新文件名
}
type Filepath struct {
//O_FilePath string `form:"oldpath" json:"oldpath" binding:"required"` // 旧文件路径
N_FilePath string `form:"newpath" json:"newpath" binding:"required"` // 新文件路径
}
type FileUpload struct {
FileName string `form:"file_name" json:"file_name" binding:"required"` // 文件名必填
SavePath string `form:"save_path" json:"save_path" binding:"required"` // 保存路径必填
}

@ -0,0 +1,35 @@
package file
import (
"os"
"path/filepath"
)
func CreateFile(userName, filePath string) error {
// 获取main.go所在的目录
currentDir, err := os.Getwd()
if err != nil {
return err
}
baseDir := filepath.Join(currentDir, "file_library")
userDir := filepath.Join(baseDir, userName)
// 检查userName对应的文件夹是否存在如果不存在则创建
err = os.MkdirAll(userDir, 0755)
if err != nil {
return err
}
fullFilePath := filepath.Join(baseDir, userName, filePath)
file, err := os.Create(fullFilePath)
if err != nil {
return err
}
defer file.Close()
return nil
}

@ -0,0 +1,32 @@
package file
import (
"os"
"path/filepath"
)
func FileDe(userName, filePath string) error {
currentDir, err := os.Getwd()
if err != nil {
return err
}
baseDir := filepath.Join(currentDir, "file_library")
fullFilePath := filepath.Join(baseDir, userName, filePath)
// 判断文件是否存在
_, err = os.Stat(fullFilePath)
if err != nil {
if os.IsNotExist(err) {
return os.ErrNotExist
}
return err
}
err = os.Remove(fullFilePath)
if err != nil {
return err
}
return nil
}

@ -0,0 +1,30 @@
package file
import (
"encoding/base64"
"os"
"path/filepath"
)
// 打开文件将对应文件转换为base64格式后返回前端
func GetFile(userName, filePath string) (string, error) {
// 获取main.go所在的目录
currentDir, err := os.Getwd()
if err != nil {
return "", err
}
baseDir := filepath.Join(currentDir, "file_library")
fullFilePath := filepath.Join(baseDir, userName, filePath)
fileContent, err := os.ReadFile(fullFilePath)
if err != nil {
return "", err
}
// 将文件内容转换为base64格式
fileContentBase64 := base64.StdEncoding.EncodeToString(fileContent)
return fileContentBase64, nil
}

@ -0,0 +1,40 @@
package file
import (
"os"
"path/filepath"
)
func FileMove(userName, O_FilePath, N_FilePath string) error {
// 获取main.go所在的目录
currentDir, err := os.Getwd()
if err != nil {
return err
}
baseDir := filepath.Join(currentDir, "file_library")
oldFullFilePath := filepath.Join(baseDir, userName, O_FilePath)
newFullFilePath := filepath.Join(baseDir, userName, N_FilePath)
// 先判断源文件是否存在
_, err = os.Stat(oldFullFilePath)
if err != nil {
return err
}
// 判断目标文件路径的上级目录是否存在,如果不存在则创建(确保目标路径合法)
targetDir := filepath.Dir(newFullFilePath)
err = os.MkdirAll(targetDir, 0755)
if err != nil {
return err
}
// 使用os.Rename进行文件移动操作
err = os.Rename(oldFullFilePath, newFullFilePath)
if err != nil {
return err
}
return nil
}

@ -0,0 +1,28 @@
package file
import (
"os"
"path/filepath"
)
func FileRename(userName, filePath, newFilePath string) error {
// 获取main.go所在的目录
currentDir, err := os.Getwd()
if err != nil {
return err
}
baseDir := filepath.Join(currentDir, "file_library")
oldfullFilePath := filepath.Join(baseDir, userName, filePath)
newfullFilePath := filepath.Join(baseDir, userName, newFilePath)
err = os.Rename(oldfullFilePath, newfullFilePath)
if err != nil {
return err
}
return nil
}

@ -0,0 +1,56 @@
package file
import (
"encoding/base64"
"fmt"
"os"
"path/filepath"
)
func FileSave(userName, savePath, fileDataBase64 string) error {
currentDir, err := os.Getwd()
if err != nil {
return err
}
baseDir := filepath.Join(currentDir, "file_library")
fullFilePath := filepath.Join(baseDir, userName, savePath)
// 创建文件所在目录(如果不存在)
dir := filepath.Dir(fullFilePath)
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
// 记录开始解码
fmt.Println("开始对文件数据进行Base64解码...")
fileData, err := base64.StdEncoding.DecodeString(fileDataBase64)
if err != nil {
// 记录解码失败及错误信息
fmt.Printf("Base64解码失败错误信息%v\n", err)
return err
}
// 记录解码成功
fmt.Println("Base64解码成功")
// 创建目标文件前记录日志
fmt.Printf("准备创建文件:%s\n", fullFilePath)
targetFile, err := os.Create(fullFilePath)
if err != nil {
// 记录创建文件失败及错误信息
fmt.Printf("创建文件失败,错误信息:%v\n", err)
return err
}
defer targetFile.Close()
// 写入文件内容前记录日志
fmt.Println("准备写入文件内容")
_, err = targetFile.Write(fileData)
if err != nil {
// 记录写入文件内容失败及错误信息
fmt.Printf("写入文件内容失败,错误信息:%v\n", err)
return err
}
fmt.Println("文件内容写入成功")
return nil
}

@ -0,0 +1,26 @@
package file
import (
"os"
"path/filepath"
)
func CreateFolder(userName, folderPath string) error {
// 获取main.go所在的目录
currentDir, err := os.Getwd()
if err != nil {
return err
}
baseDir := filepath.Join(currentDir, "file_library")
fullFolderPath := filepath.Join(baseDir, userName, folderPath)
err = os.MkdirAll(fullFolderPath, 0755)
if err != nil {
return err
}
return nil
}

@ -0,0 +1,40 @@
package file
import (
"os"
"path/filepath"
)
// 存储文件夹中每个文件或文件夹的相关信息
type FolderContentInfo struct {
Name string `json:"name"`
IsDir bool `json:"isDir"`
Path string `json:"path"`
}
// 遍历并返回指定文件夹下的内容
func GetFolder(userName, folderPath string) ([]FolderContentInfo, error) {
var contents []FolderContentInfo
baseDir, err := os.Getwd()
if err != nil {
return nil, err
}
fullFolderPath := filepath.Join(baseDir, "file_library", userName, folderPath)
fileInfos, err := os.ReadDir(fullFolderPath)
if err != nil {
return nil, err
}
for _, fileInfo := range fileInfos {
relativePath := fileInfo.Name()
contentInfo := FolderContentInfo{
Name: fileInfo.Name(),
IsDir: fileInfo.IsDir(),
Path: relativePath,
}
contents = append(contents, contentInfo)
}
return contents, nil
}

@ -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"

@ -0,0 +1 @@
基于大小模型协同的在线编辑器

@ -0,0 +1 @@
基于大小模型协同的在线编辑器

@ -87,6 +87,38 @@ 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"))
}
file := backend.Group("file/")
{
//创建文件
file.POST("file_creat", validatorFactory.Create(consts.ValidatorPrefix+"FileCreat"))
//打开文件
file.GET("file_get", validatorFactory.Create(consts.ValidatorPrefix+"FileGet"))
//文件重命名
file.POST("file_rename", validatorFactory.Create(consts.ValidatorPrefix+"FileRename"))
//移动文件
file.POST("file_move", validatorFactory.Create(consts.ValidatorPrefix+"FileMove"))
//保存文件
file.POST("file_save", validatorFactory.Create(consts.ValidatorPrefix+"FileSave"))
//删除文件
file.POST("file_delete", validatorFactory.Create(consts.ValidatorPrefix+"FileDelete"))
//创建文件夹
file.POST("folder_creat", validatorFactory.Create(consts.ValidatorPrefix+"FolderCreat"))
//打开文件夹
file.GET("folder_get", validatorFactory.Create(consts.ValidatorPrefix+"FolderGet"))
}
// 【需要token】中间件验证的路由
backend.Use(authorization.CheckTokenAuth())
{
@ -107,6 +139,7 @@ func InitWebRouter_Co() *gin.Engine {
users.POST("logout", validatorFactory.Create(consts.ValidatorPrefix+"UsersLogout"))
}
}
}
return router
}

@ -1,4 +1,5 @@
请将以下文章进行重新排版,使其格式更加美观且井然有序,同时保留文章的所有原始内容和文字,不要对内容进行任何修改。
请你调整标题样式,将所有标题改为清晰的分级结构。文章标题使用#,一级标题使用##,以此类推。
正文部分的段落需适当分段,避免过长或过短。 请确保整体排版整洁、易于阅读。
所有的图片已被替换为"tag图片1"、"tag图片2"等在处理时不要考虑图片本身不要删除这些tag。
不要对文章的原始内容进行修改。只返回重新排版后的内容,不要返回其他的额外内容。

@ -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"
}
]

@ -20,7 +20,7 @@
"jquery": "^3.7.1",
"jwt-decode": "^4.0.0",
"mitt": "^3.0.1",
"vue": "^3.2.13",
"vue": "^3.5.13",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"
},
@ -8689,7 +8689,7 @@
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
},
"node_modules/mkdirp": {

File diff suppressed because it is too large Load Diff

@ -1,84 +1,125 @@
// 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';
import axios from 'axios';
// 导入ModulerUser模块
import ModulerUser from '../store/user.js';
// 获取用户配置
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
@ -92,6 +133,9 @@ export function getUserConfigFromBackend() {
};
}
function isBase64(str) {
return /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/.test(str);
}
// TODO 实现自动保存saveData方法将编辑内容发送至后端
export function saveData(data) {
// return new Promise( resolve => {
@ -103,6 +147,65 @@ export function saveData(data) {
// } );
console.log('saving...');
console.log(data);
let encodedData;
if (typeof data ==='string') {
// 去除HTML标签将内容转换为纯文本
const textWithoutTags = data.replace(/<[^>]*>/g, '');
// 使用btoa进行Base64编码同时处理可能出现的编码异常情况比如包含非ASCII字符等
try {
encodedData = btoa(unescape(encodeURIComponent(textWithoutTags)));
} catch (e) {
console.error('对文本内容进行Base64编码时出错:', e);
throw new Error('数据编码失败,请检查数据内容');
}
} else {
console.error('不支持的数据类型,请传入文本内容');
throw new Error('不支持的数据类型');
}
if (!isBase64(encodedData)) {
console.error('Base64编码后的数据格式不正确');
throw new Error('数据格式错误');
}
// 从ModulerUser模块的状态中获取相关信息
const { username, path } = ModulerUser.state;
const fileName = 'yourFileNameHere'; // 这里需要替换为实际的文件名
// path是相对用户主目录的路径各部分组成的数组拼接成合适的相对路径字符串
const savePath = path.join('/');
const formData = new FormData();
formData.append('user_name', username);
formData.append('file_name', fileName);
formData.append('save_path', savePath);
formData.append('file_data', encodedData);
return axios.post('http://localhost:14514/admin/file/file_save', formData, {
headers: {
'Content-Type': 'multipart/form-data',
//'Authorization': `Bearer ${ModulerUser.state.access}`
}
})
.then(response => {
if (response.status === 200 && response.data.code === 200 && response.data.data === "文件保存成功") {
console.log('文件保存成功,返回信息:', response.data);
return response.data;
} else {
console.error('文件保存失败,返回信息:', response.data);
throw new Error(`文件保存失败,返回信息: ${JSON.stringify(response.data)}`);
}
})
.catch(error => {
if (error.response && error.response.status === 400) {
console.error('文件保存出现问题,可能是请求参数或后端业务逻辑执行出错,请检查相关信息');
} else {
console.error('保存文件出现其他错误:', error);
}
throw error;
});
}
@ -142,7 +245,7 @@ export function getAndApplyUserStyles() {
// markdown转html 便于将大语言模型的输出一般为markdown格式转换为ckeditor的html格式
// 利用ckeditor markdown插件的功能子类但不能在CkeditorView.vue中直接使用markdown插件
// 否则会改变编辑器数据处理器为markdown即getData()需要传入markdown stringsetData()返回markdown string
export function markdown2html(markdownString){
export function markdown2html(markdownString) {
const markdownToHtml = new MarkdownToHtml();
const htmlString = markdownToHtml.parse(markdownString);
return htmlString;
@ -150,8 +253,119 @@ export function markdown2html(markdownString){
// html转markdown 便于将文件内容发送给大语言模型来进行排版
// 再利用 @markdown2html 将大语言模型的返回内容重新转换为文件内容并展示 或做进一步处理
export function html2markdown(htmlString){
const htmltomarkdown = new HtmlToMarkdown();
const markdownString = htmltomarkdown.parse(htmlString);
return markdownString
export function html2markdown(htmlString) {
const htmltomarkdown = new HtmlToMarkdown();
const markdownString = htmltomarkdown.parse(htmlString);
return markdownString
}
// 请求用户AI生成的样式配置
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: options.titleStyleOption
},
headingStyle: {
option: options.headingStyleOption
},
bodyStyle: {
option: options.bodyStyleOption
},
blockquoteStyle: {
option: options.blockquoteStyleOption
},
codeBlockStyle: {
option: options.codeBlockStyleOption
},
listStyle: {
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);
}
}

@ -0,0 +1,240 @@
/* default */
/* title style */
/* 标题样式一 黑体二号粗体 */
.ck-content .title1{
font-family: '黑体';
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{
counter-increment: heading1counter;
/* (一)(二) */
content: "" counter(heading1counter, cjk-ideographic) " ";
}
.ck-content .heading2::before{
counter-increment: heading2counter;
/* 一、 二、 */
content: counter(heading2counter, cjk-ideographic) "、 ";
}
.ck-content .heading3::before{
counter-increment: heading3counter;
/* 1. 2. */
content: counter(heading3counter) ". ";
}
.ck-content .heading4::before{
counter-increment: heading4counter;
/* 1) 2) */
content: counter(heading4counter) ") ";
}
.ck-content .heading5::before{
counter-increment: heading5counter;
/* 第一章 第二章 */
content: "第" counter(heading5counter, cjk-ideographic) "章 ";
}
.ck-content .heading6::before{
counter-increment: heading6counter;
/* 第一小节 第二小节 */
content: "第" counter(heading6counter, cjk-ideographic) "小节 ";
}
.ck-content .heading7::before{
counter-increment: heading7counter;
/* I. II. */
content: counter(heading7counter, upper-roman) ". ";
}
.ck-content .heading8::before{
counter-increment: heading8counter;
/* A. B. */
content: counter(heading8counter,upper-alpha) ". ";
}
.ck-content .heading9::before{
counter-increment: heading9counter;
/* a. b. */
content: counter(heading9counter,lower-alpha) ". ";
}
.ck-content .heading10::before{
counter-increment: heading10counter;
/* i. ii. */
content: counter(heading10counter, lower-roman) ". ";
}
/*style插件样式*/
.ck-content h3.category {
font-family: 'Oswald';
font-size: 20px;
font-weight: bold;
color: #555;
letter-spacing: 10px;
margin: 0;
padding: 0;
}
.ck-content h2.document-title {
font-family: 'Oswald';
font-size: 50px;
font-weight: bold;
margin: 0;
padding: 0;
border: 0;
}
.ck-content h3.document-subtitle {
font-family: 'Oswald';
font-size: 20px;
color: #555;
margin: 0 0 1em;
font-weight: bold;
padding: 0;
}
.ck-content p.info-box {
--background-size: 30px;
--background-color: #e91e63;
padding: 1.2em 2em;
border: 1px solid var(--background-color);
background: linear-gradient(135deg,
var(--background-color) 0%,
var(--background-color) var(--background-size),
transparent var(--background-size)),
linear-gradient(135deg,
transparent calc(100% - var(--background-size)),
var(--background-color) calc(100% - var(--background-size)),
var(--background-color));
border-radius: 10px;
margin: 1.5em 2em;
box-shadow: 5px 5px 0 #ffe6ef;
}
.ck-content blockquote.side-quote {
font-family: 'Oswald';
font-style: normal;
float: right;
width: 35%;
position: relative;
border: 0;
overflow: visible;
z-index: 1;
margin-left: 1em;
}
.ck-content blockquote.side-quote::before {
content: '“';
position: absolute;
top: -37px;
left: -10px;
display: block;
font-size: 200px;
color: #e7e7e7;
z-index: -1;
line-height: 1;
}
.ck-content blockquote.side-quote p {
font-size: 2em;
line-height: 1;
}
.ck-content blockquote.side-quote p:last-child:not(:first-child) {
font-size: 1.3em;
text-align: right;
color: #555;
}
.ck-content span.marker {
background: yellow;
}
.ck-content span.spoiler {
background: #000;
color: #000;
}
.ck-content span.spoiler:hover {
background: #000;
color: #fff;
}
.ck-content pre.fancy-code {
border: 0;
margin-left: 2em;
margin-right: 2em;
border-radius: 10px;
}
.ck-content pre.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-content pre.fancy-code-dark {
background: #272822;
color: #fff;
box-shadow: 5px 5px 0 #0000001f;
}
.ck-content pre.fancy-code-bright {
background: #dddfe0;
color: #000;
box-shadow: 5px 5px 0 #b3b3b3;
}
/* list style */
.ck-content ul.disc {
list-style-type: disc;
}
.ck-content ul.circle {
list-style-type: circle;
}
.ck-content ul.square {
list-style-type: square;
}
/* body style */
.ck-content p.normal-text {
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-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;
}

@ -91,191 +91,4 @@
.main-container.sidebar-open .editor-container__editor-wrapper {
transition: margin-left 0.3s ease;
margin-left: 350px;
}
/*用户定义的style插件样式*/
.ck-content h3.category {
font-family: 'Oswald';
font-size: 20px;
font-weight: bold;
color: #555;
letter-spacing: 10px;
margin: 0;
padding: 0;
}
.ck-content h2.document-title {
font-family: 'Oswald';
font-size: 50px;
font-weight: bold;
margin: 0;
padding: 0;
border: 0;
}
.ck-content h3.document-subtitle {
font-family: 'Oswald';
font-size: 20px;
color: #555;
margin: 0 0 1em;
font-weight: bold;
padding: 0;
}
.ck-content p.info-box {
--background-size: 30px;
--background-color: #e91e63;
padding: 1.2em 2em;
border: 1px solid var(--background-color);
background: linear-gradient(135deg,
var(--background-color) 0%,
var(--background-color) var(--background-size),
transparent var(--background-size)),
linear-gradient(135deg,
transparent calc(100% - var(--background-size)),
var(--background-color) calc(100% - var(--background-size)),
var(--background-color));
border-radius: 10px;
margin: 1.5em 2em;
box-shadow: 5px 5px 0 #ffe6ef;
}
.ck-content blockquote.side-quote {
font-family: 'Oswald';
font-style: normal;
float: right;
width: 35%;
position: relative;
border: 0;
overflow: visible;
z-index: 1;
margin-left: 1em;
}
.ck-content blockquote.side-quote::before {
content: '“';
position: absolute;
top: -37px;
left: -10px;
display: block;
font-size: 200px;
color: #e7e7e7;
z-index: -1;
line-height: 1;
}
.ck-content blockquote.side-quote p {
font-size: 2em;
line-height: 1;
}
.ck-content blockquote.side-quote p:last-child:not(:first-child) {
font-size: 1.3em;
text-align: right;
color: #555;
}
.ck-content span.marker {
background: yellow;
}
.ck-content span.spoiler {
background: #000;
color: #000;
}
.ck-content span.spoiler:hover {
background: #000;
color: #fff;
}
.ck-content pre.fancy-code {
border: 0;
margin-left: 2em;
margin-right: 2em;
border-radius: 10px;
}
.ck-content pre.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-content pre.fancy-code-dark {
background: #272822;
color: #fff;
box-shadow: 5px 5px 0 #0000001f;
}
.ck-content pre.fancy-code-bright {
background: #dddfe0;
color: #000;
box-shadow: 5px 5px 0 #b3b3b3;
}
.ck-content p.gradientborder {
--borderWidth: 12px;
--bRadius: 5px;
width: 60%;
height: 60%;
position: relative;
z-index: 0;
overflow: hidden;
padding: 2rem;
z-index: 0;
border-radius: --bRadius;
&::after,
&::before {
box-sizing: border-box;
}
&::before {
content: '';
position: absolute;
left: -50%;
top: -50%;
width: 200%;
height: 200%;
z-index: -2;
background-repeat: no-repeat;
background-size: 50% 50%, 50% 50%;
background-position: 0 0, 100% 0, 100% 100%, 0 100%;
background-image: linear-gradient(#399953, #399953), linear-gradient(#fbb300, #fbb300), linear-gradient(#d53e33, #d53e33), linear-gradient(#377af5, #377af5);
animation: rotate 4s linear infinite;
@keyframes rotate {
100% {
transform: rotate(1turn);
}
}
}
&::after {
content: '';
position: absolute;
z-index: -1;
left: calc(var(--borderWidth) / 2);
top: calc(var(--borderWidth) / 2);
width: calc(100% - var(--borderWidth));
height: calc(100% - var(--borderWidth));
background: white;
border-radius: --bRadius;
/* 这一行是为了方便查看原来的样子的 */
animation: opacityChange 3s infinite alternate;
}
@keyframes opacityChange {
50% {
opacity: 1;
}
100% {
opacity: .5;
}
}
}

@ -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">
@ -28,9 +28,9 @@
<el-menu-item index="1-5" @click="showRefineDOCContent('translation')"></el-menu-item>
</el-sub-menu>
<el-sub-menu index="2" class="horizontal-sub-menu">
<template #title>图文转换</template>
<el-menu-item index="2-1" @click="showContent('ocr')">OCR</el-menu-item>
<el-menu-item index="2-2" @click="showContent('mindmap')"></el-menu-item>
<template #title>智能识别</template>
<el-menu-item index="2-1" @click="showAiRecgContent('pic_recognition')"></el-menu-item>
<!-- <el-menu-item index="2-2" @click="showAiRecgContent('voc_recognition')"></el-menu-item>-->
</el-sub-menu>
<el-sub-menu index="3" class="horizontal-sub-menu">
<template #title>样式生成</template>
@ -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,11 +58,26 @@
<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>
<div v-for="(item, index) in currentAiRecgContent" :key="index" class="messages">
<div class="message-answer">
<pre>{{ item.newContent }}</pre>
</div>
</div>
</div>
</div>
<div v-if="currentContent === 'ocr'">OCR</div>
<div v-if="currentContent === 'mindmap'"></div>
<div v-if="currentContent === 'manual'">
<!-- 手动输入表单 -->
<el-form @submit.prevent="submitForm">
@ -300,6 +322,7 @@ import {
} from 'ckeditor5';
import 'ckeditor5/ckeditor5.css';
import '../public/sidebar.css';
import '../public/generatedStyle.css';
import { ElButton, ElInput, ElSelect, ElOption, ElForm, ElFormItem, ElMenu, ElMenuItem, ElColorPicker, ElSubMenu } from 'element-plus';
import emitter, { setConfig } from '../components/plugins'
// import {getUserConfigFromBackend,saveData,getPageContent,getAndApplyUserStyles} from './components/utils';
@ -320,6 +343,7 @@ export default {
isNavbarOpen: false,//
currentContent: '', //
currentRefineDocContent: [], //
currentAiRecgContent: [], //
formData: {
fontFamily: '',
color: '',
@ -345,10 +369,13 @@ export default {
this.$refs.editorToolbarElement.appendChild(editor.ui.view.toolbar.element);
this.$refs.editorMenuBarElement.appendChild(editor.ui.view.menuBarView.element);
//
// DFZ
const pageContent = this.store.state.user.filecontent;
editor.setData(pageContent);
// editorwindow便使|
window.editor = editor;
window.username = this.store.state.user.username;
// CSS DFZ
},
// sidebar
// /
@ -361,20 +388,34 @@ 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], []);
}
},
clearRefineDOCContent(){
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], []);
}
},
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() {
this.store.commit('clearContentsForTag', this.store.getters.getCurrentTag);
if (this.store.getters.getCurrentContent) {
this.currentAiRecgContent = this.store.getters.getCurrentContent.reduce((acc, current) => [current, ...acc], []);
}
},
//
// DFZ
submitForm() {
const selectedStyles = {};
for (const key in this.formData) {
@ -393,7 +434,36 @@ export default {
cssClass += `}`;
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("类名不能为空!");
}
@ -455,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'
@ -572,17 +642,37 @@ export default {
saveButton.onclick = () => {
// save
// TODO
// DFZ
// 使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);
@ -631,16 +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)
});
},
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
@ -652,6 +748,7 @@ export default {
},
beforeUnmount() {
emitter.off('show-refine-doc-sidebar');
emitter.off('show-ai-recg-sidebar');
}
};
</script>

@ -93,6 +93,7 @@ export default {
}
else {
axios({
// DFZ
url:' http://1.94.171.222:8000/operation/open/',
type:'get',
params:{

@ -44,7 +44,7 @@ export default {
const register = () => {
$.ajax({
url:'http://47.106.113.194:8000/register/',
url:'http://1.94.171.222:8000/register/',
type:'post',
data:{
username:username.value,

Loading…
Cancel
Save