lc 2 months ago
commit 411a8d42b2

@ -229,7 +229,7 @@ voc|form-data|string|必填|"" (示例内容在 storage/app/test/A13_221.txt 中
---|---|---|---|---
doc|form-data|string|必填|""
background|form-data|string||""
type|form-data|string|必填|"abstract"/"decorate"/"sequel_writing"/"rewrite_wrong"/"translate"
type|form-data|string|必填|"summary"/"decoration"/"correction"/"extension"/"translation"
> 返回示例:
```json
{

@ -87,4 +87,6 @@ const (
DocRefineFailCode int = -400452
StyleGenerateFailMsg string = "样式生成失败"
StyleGenerateFailCode int = -400453
LayoutGenerateFailMsg string = "排版生成失败"
LayoutGenerateFailCode int = -400454
)

@ -9,6 +9,8 @@ import (
type StyleGenerate struct {
}
type LayoutGenerate struct {
}
// ai生成样式
func (s *StyleGenerate) StyleGenerate(c *gin.Context) {
@ -29,3 +31,15 @@ func (s *StyleGenerate) StyleGenerate(c *gin.Context) {
}
}
// ai排版
func (l *LayoutGenerate) LayoutGenerate(c *gin.Context) {
// 流式传输
// 设置 HTTP 头部为 SSE
c.Writer.Header().Set("Content-Type", "text/event-stream")
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive")
if err := ai_model_cli.RequestLayout(c); err != nil {
response.Fail(c, consts.LayoutGenerateFailCode, consts.LayoutGenerateFailMsg, err)
}
}

@ -16,7 +16,7 @@ type DocRefine struct {
Background string `form:"background" json:"background" `
}
var Types = []string{"abstract", "decorate", "sequel_writing", "rewrite_wrong", "translate"}
var Types = []string{"summary", "decoration", "extension", "correction", "translation"}
func (d DocRefine) CheckParams(context *gin.Context) {
if err := context.ShouldBind(&d); err != nil {
@ -24,6 +24,7 @@ func (d DocRefine) CheckParams(context *gin.Context) {
response.ValidatorError(context, err)
return
}
// 判断是否在类型中
t := d.Type
isExit := false

@ -1,12 +1,23 @@
package ai_layout
import(
"github.com/gin-gonic/gin"
// "goskeleton/app/utils/response"
// "goskeleton/app/http/controller/web"
// "goskeleton/app/http/validator/core/data_transfer"
// "goskeleton/app/global/consts"
"goskeleton/app/utils/response"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/global/consts"
)
type LayoutGenerate struct {
DocContent string `form:"doc_content" json:"doc_content" binging:"required"`
}
func (s LayoutGenerate) CheckParams(context *gin.Context) {
func (l LayoutGenerate) CheckParams(context *gin.Context) {
if err:=context.ShouldBind(&l);err!=nil{
response.ValidatorError(context,err)
return
}
extraAddBindDataContext := data_transfer.DataAddContext(l, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "LayoutGenerate表单参数验证器json化失败", "")
return
}
(&web.LayoutGenerate{}).LayoutGenerate(extraAddBindDataContext)
}

@ -2,7 +2,6 @@ package ai_model_cli
import (
"context"
"fmt"
"goskeleton/app/global/variable"
"strings"
@ -25,18 +24,17 @@ func RequestQianFan(cont *gin.Context) (r bool, c interface{}) {
b := strings.TrimSpace(cont.PostForm("background"))
d := strings.TrimSpace(cont.PostForm("doc"))
switch t {
case "abstract":
case "summary":
message = "请在这个背景下:" + b + "\n对下文进行概括不要改变原有语言" + d
case "decorate":
case "decoration":
message = "请在这个背景下:" + b + "\n对下文进行润色不要改变原有语言" + d
case "sequel_writing":
case "extension":
message = "请在这个背景下:" + b + "\n对下文进行续写不要改变原有语言" + d
case "rewrite_wrong":
case "correction":
message = "请在这个背景下:" + b + "\n对下文进行改错不要改变原有语言" + d
case "translate":
case "translation":
message = "请在这个背景下:" + b + "\n对下文进行翻译" + d
}
resp, _ := chat.Do(
context.TODO(),
&qianfan.ChatCompletionRequest{
@ -45,6 +43,5 @@ func RequestQianFan(cont *gin.Context) (r bool, c interface{}) {
},
},
)
fmt.Println(resp.Result)
return true, gin.H{"new_doc": resp.Result}
}

@ -5,6 +5,7 @@ import (
"fmt"
"goskeleton/app/global/variable"
"os"
"strings"
"goskeleton/app/global/consts"
@ -84,85 +85,107 @@ func RequestStyle(c *gin.Context) (interface{}, error) {
}
func RequestStyleStream(c *gin.Context) error {
userMsg := c.GetString(consts.ValidatorPrefix + "user_input")
qianfan.GetConfig().AccessKey = variable.ConfigYml.GetString("BaiduCE.QianFanAccessKey")
qianfan.GetConfig().SecretKey = variable.ConfigYml.GetString("BaiduCE.QianFanSecretKey")
chat := qianfan.NewChatCompletion(
qianfan.WithModel("ERNIE-4.0-8K"),
)
chatHistory := []qianfan.ChatCompletionMessage{}
systemMsgPath := variable.ConfigYml.GetString("BaiduCE.StyleGeneratePromptPath")
prompt, err := os.ReadFile(variable.BasePath + systemMsgPath)
if err != nil || len(prompt) == 0 {
variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err))
return err
}
userHistory, exist := c.Get(consts.ValidatorPrefix + "chat_history")
if exist && userHistory != nil {
historySlice, ok := userHistory.([]interface{})
if !ok || len(historySlice)%2 != 0 {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
var chatHistoryConverted []qianfan.ChatCompletionMessage
for _, item := range historySlice {
if itemMap, ok := item.(map[string]interface{}); ok {
role, roleOk := itemMap["role"].(string)
content, contentOk := itemMap["content"].(string)
if roleOk && contentOk {
chatHistoryConverted = append(chatHistoryConverted, qianfan.ChatCompletionMessage{
Role: role,
Content: content,
})
} else {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\nrole 或 content 类型断言失败", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
} else {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\n无法将 item 转换为 map[string]interface{}", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
}
if len(chatHistoryConverted) > 0 && len(chatHistoryConverted)%2 == 0 {
chatHistory = append(chatHistory, chatHistoryConverted...)
}
}
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage(userMsg))
stream, err := chat.Stream(context.TODO(), &qianfan.ChatCompletionRequest{System: string(prompt), Messages: chatHistory})
if err != nil {
variable.ZapLog.Error(fmt.Sprintf("对话失败: %v", err))
return err
}
defer stream.Close()
userMsg := c.GetString(consts.ValidatorPrefix + "user_input")
chatHistory := []qianfan.ChatCompletionMessage{}
systemMsgPath := variable.ConfigYml.GetString("BaiduCE.StyleGeneratePromptPath")
prompt, err := os.ReadFile(variable.BasePath + systemMsgPath)
if err != nil || len(prompt) == 0 {
variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err))
return err
}
userHistory, exist := c.Get(consts.ValidatorPrefix + "chat_history")
if exist && userHistory != nil {
historySlice, ok := userHistory.([]interface{})
if !ok || len(historySlice)%2 != 0 {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
var chatHistoryConverted []qianfan.ChatCompletionMessage
for _, item := range historySlice {
if itemMap, ok := item.(map[string]interface{}); ok {
role, roleOk := itemMap["role"].(string)
content, contentOk := itemMap["content"].(string)
if roleOk && contentOk {
chatHistoryConverted = append(chatHistoryConverted, qianfan.ChatCompletionMessage{
Role: role,
Content: content,
})
} else {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\nrole 或 content 类型断言失败", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
} else {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\n无法将 item 转换为 map[string]interface{}", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
}
if len(chatHistoryConverted) > 0 && len(chatHistoryConverted)%2 == 0 {
chatHistory = append(chatHistory, chatHistoryConverted...)
}
}
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage(userMsg))
return ChatByStream(c, string(prompt), chatHistory)
}
func RequestLayout(c *gin.Context) error {
doc_content := c.GetString(consts.ValidatorPrefix + "doc_content")
chatHistory := []qianfan.ChatCompletionMessage{}
systemMsgPath := variable.ConfigYml.GetString("BaiduCE.LayoutGeneratePromptPath")
prompt, err := os.ReadFile(variable.BasePath + systemMsgPath)
if err != nil || len(prompt) == 0 {
variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err))
return err
}
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage("待排版内容\n"+doc_content))
return ChatByStream(c, string(prompt), chatHistory)
}
func ChatByStream(c *gin.Context, prompt string, chatHistory []qianfan.ChatCompletionMessage) error{
qianfan.GetConfig().AccessKey = variable.ConfigYml.GetString("BaiduCE.QianFanAccessKey")
qianfan.GetConfig().SecretKey = variable.ConfigYml.GetString("BaiduCE.QianFanSecretKey")
chat := qianfan.NewChatCompletion(
qianfan.WithModel("ERNIE-4.0-8K"),
)
stream, err := chat.Stream(context.TODO(), &qianfan.ChatCompletionRequest{System: string(prompt), Messages: chatHistory})
if err != nil {
variable.ZapLog.Error(fmt.Sprintf("对话失败: %v", err))
return err
}
defer stream.Close()
c.Writer.Flush()
defer c.Writer.Flush()
for {
response, err := stream.Recv()
if response.IsEnd {
break // 流结束,退出循环
}
if err != nil {
variable.ZapLog.Error(fmt.Sprintf("接收流失败: %v", err))
return err
}
outputMsg:=strings.Builder{}
for {
response, err := stream.Recv()
if response.IsEnd {
break // 流结束,退出循环
}
if err != nil {
variable.ZapLog.Error(fmt.Sprintf("接收流失败: %v", err))
return err
}
// 将结果写入到响应体
if _,err:=fmt.Fprintf(c.Writer,"%s",response.Result);err!=nil{
outputMsg.WriteString(response.Result)
if _, err := fmt.Fprintf(c.Writer, "%s", response.Result); err != nil {
variable.ZapLog.Error(fmt.Sprintf("写入流失败: %v", err))
return err
}
// 立即刷新缓冲区,以确保数据立即发送到客户端
c.Writer.Flush()
}
return nil // 正常结束,返回 nil
}
return nil // 正常结束,返回 nil
}

@ -154,3 +154,4 @@ BaiduCE:
# QianFanSecretKey: "1edf17c358574e75b9913ebff7d95b61" # 访问千帆sdk 时用的 SecretKey
QianFanSecretKey: "cb812e1b6e56420ea858d160e1351869"
StyleGeneratePromptPath: "/storage/app/prompt/style_generate.prompt" # 生成样式的提示词保存路径
LayoutGeneratePromptPath: "/storage/app/prompt/layout_generate.prompt" # 生成布局的提示词保存路径

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

@ -19,7 +19,8 @@
"html-docx-js-typescript": "^0.1.5",
"jquery": "^3.7.1",
"jwt-decode": "^4.0.0",
"vue": "^3.5.13",
"mitt": "^3.0.1",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"
},
@ -8686,6 +8687,11 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",

@ -19,6 +19,7 @@
"html-docx-js-typescript": "^0.1.5",
"jquery": "^3.7.1",
"jwt-decode": "^4.0.0",
"mitt": "^3.0.1",
"vue": "^3.5.13",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"

@ -83,9 +83,10 @@ import {
getPageContent,
getUserConfigFromBackend,
saveData,
// markdown2html
// markdown2html,
html2markdown
} from './utils';
import mitt from 'mitt';
// 导出为docx插件
function exportWord(){
const pageContent = getPageContent();
@ -198,9 +199,56 @@ class Export2PDF extends Plugin {
}
}
// 智能润色插件
class Translation extends Plugin {
init() {
// 智能润色发送消息
function sendDORMsg(type, fullText) {
// 获取选中的文本,用来进行后续操作
const selectionText = window.getSelection().toString();
if (selectionText.trim() === '') return;
const formData = new FormData();
formData.append('doc', selectionText);
formData.append('background', fullText);
formData.append('type', type);
const requestOptions= {
method: 'POST',
body: formData
};
console.log("formData:", formData);
const store = window.store;
const res = {oldContent:selectionText, newContent:'...'};
store.commit('setCurrentTag', type);
store.commit('addContentToTag', {tag:type, newContent:res});
const index = store.getters.getCurrentindex;
console.log("index", index);
emitter.emit('show-refine-doc-sidebar', type);
fetch("/web_api/admin/ai_doc/doc_refine", requestOptions)
.then(response => {
if (!response.ok) {
throw new Error("请求出错");
}
// 根据返回的数据格式进行相应处理这里假设返回的数据是JSON格式所以使用response.json()解析
return response.json();
})
.then(data => {
// console.log("useStore", store);
const newRes = {oldContent:selectionText, newContent:data.data.new_doc};
store.commit('changeContentForTag', {tag:type, index:index, newContent:newRes});
emitter.emit('show-refine-doc-sidebar', type);
})
.catch(error => {
console.error("POST请求出错", error);
});
}
class RefineDoc extends Plugin {
init() {
// console.log('Translation initialized!');
this.editor.ui.componentFactory.add('translate', (locale) => {
@ -254,19 +302,14 @@ class Translation extends Plugin {
addListToDropdown(dropdownView, items);
dropdownView.on('execute', (eventInfo) => {
const { id, label } = eventInfo.source;
// 获取选中的文本,用来进行后续操作
const selectionText = window.getSelection().toString();
if (id === 'summary') {
// this.editor.execute('ExportToWord');
console.log('Object (en):', label, selectionText);
}
});
const id = eventInfo.source.id;
sendDORMsg(id, '');
});
return dropdownView;
});
}
}
return dropdownView;
});
}
}
// 侧边栏按钮
class ToggleSideBar extends Plugin {
@ -340,7 +383,58 @@ class SaveButton extends Plugin {
});
}
}
// AI 自动排版
function aiformat(){
console.log("ai formatting")
const editor = window.editor;
const doc_content = editor.getData()
console.log(doc_content);
// TODO 处理html文件
// step 1 - split images and insert text tag
const markdown_content = html2markdown(doc_content)
console.log(markdown_content)
// TODO 请求大模型
// step 2 - convert markdown response to html text and setData
// step 3 - recover original images
// step 4 - fetch users config
// step 5 - apply title styles of user
// step 6 - apply others styles of user
}
class AiFormat extends Plugin {
init() {
const editor = this.editor;
editor.ui.componentFactory.add('AiFormat', () => {
// The button will be an instance of ButtonView.
const button = new ButtonView();
button.set({
label: '自动排版',
// withText: true
tooltip: true,
// 图标 直接插入svg文件
icon: '<svg t="1733151966769" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3854" width="200" height="200"><path d="M85.312 938.688V85.312h59.776v853.376h-59.712z m174.976-118.4a64 64 0 0 1-64-64v-128a64 64 0 0 1 64-64h614.4a64 64 0 0 1 64 64v128a64 64 0 0 1-64 64h-614.4z m0-357.376a64 64 0 0 1-64-64v-128a64 64 0 0 1 64-64h384a64 64 0 0 1 64 64v128a64 64 0 0 1-64 64h-384z" p-id="3855" fill="#2c2c2c"></path></svg>',
keystroke: 'Shift+F'
});
// Execute a callback function when the button is clicked
button.on('execute', () => {
aiformat();
});
return button;
});
// 添加快捷键 Shift+F 保存
editor.keystrokes.set('Shift+F', (event, cancel) => {
aiformat();
cancel();
});
}
}
// 配置CKEditor5
function setConfig() {
// 获取用户的样式配置
@ -375,7 +469,7 @@ function setConfig() {
'numberedList',
'outdent',
'indent',
'|', 'ExportToWord', 'ExportToPDF', 'translate', 'SideBar', 'SaveButton'
'|', 'ExportToWord', 'ExportToPDF', 'translate', 'SideBar', 'SaveButton','AiFormat'
],
shouldNotGroupWhenFull: true
},
@ -449,9 +543,9 @@ function setConfig() {
TodoList,
Underline,
Undo,
Export2Word, Translation, Export2PDF, ToggleSideBar, SaveButton
Export2Word, RefineDoc, Export2PDF, ToggleSideBar, SaveButton, AiFormat
],
balloonToolbar: ['bold', 'italic', '|', 'link', 'insertImage', '|', 'bulletedList', 'numberedList'],
balloonToolbar: ['bold', 'italic', '|', 'link', 'insertImage', '|', 'bulletedList', 'numberedList','|','AiFormat'],
//自定义设置字体
fontFamily: {
// 自定义字体
@ -579,11 +673,12 @@ function setConfig() {
autosave: {
waitingTime: 180000, // (in ms) 3minutes
save() {
// TODO save
return saveData(getPageContent());
}
},
translations: [translations]
}
}
export { Export2Word, Export2PDF, Translation, ToggleSideBar, setConfig };
const emitter = new mitt();
export default emitter;
export {Export2Word, Export2PDF, RefineDoc, ToggleSideBar, setConfig};

@ -1,11 +1,11 @@
// utils.js
import { MarkdownToHtml } from '@ckeditor/ckeditor5-markdown-gfm/src/markdown2html/markdown2html.js';
import { HtmlToMarkdown } from '@ckeditor/ckeditor5-markdown-gfm/src/html2markdown/html2markdown.js';
// 获取用户配置
export function getUserConfigFromBackend() {
// TODO 请求用户配置
const options = {};
// 字体、字号、样式
// TODO
const {
fontFamilyOptions = [
'default',
@ -92,7 +92,7 @@ export function getUserConfigFromBackend() {
};
}
// 实现自动保存saveData方法将编辑内容发送至后端
// TODO 实现自动保存saveData方法将编辑内容发送至后端
export function saveData(data) {
// return new Promise( resolve => {
// setTimeout( () => {
@ -128,7 +128,7 @@ export function getPageContent() {
// return pageContent.outerHTML;
}
// 获取并应用用户定义的样式
// TODO 获取并应用用户定义的样式css
export function getAndApplyUserStyles() {
// 模拟从后端获取用户定义的样式
const response = fetch('/api/user-styles');
@ -140,10 +140,18 @@ export function getAndApplyUserStyles() {
}
// markdown转html 便于将大语言模型的输出一般为markdown格式转换为ckeditor的html格式
// 利用ckeditor markdown插件但不能在CkeditorView.vue中使用
// 利用ckeditor markdown插件的功能子类但不能在CkeditorView.vue中直接使用markdown插件
// 否则会改变编辑器数据处理器为markdown即getData()需要传入markdown stringsetData()返回markdown string
export function markdown2html(markdownString){
const markdownToHtml = new MarkdownToHtml();
const htmlString = markdownToHtml.parse(markdownString);
return htmlString;
}
// html转markdown 便于将文件内容发送给大语言模型来进行排版
// 再利用 @markdown2html 将大语言模型的返回内容重新转换为文件内容并展示 或做进一步处理
export function html2markdown(htmlString){
const htmltomarkdown = new HtmlToMarkdown();
const markdownString = htmltomarkdown.parse(htmlString);
return markdownString
}

@ -7,6 +7,8 @@ import { CkeditorPlugin } from '@ckeditor/ckeditor5-vue';
const app = createApp(App);
window.store = store;
app.use(router);
app.use(store);
app.use(CkeditorPlugin);

@ -124,4 +124,16 @@
padding: 10px;
border-radius: 4px;
background-color: #ecf0f1;
}
.message-answer {
margin: 5px 0;
padding: 10px;
border-radius: 4px;
background-color: rgb(189, 195, 199);
}
.message-answer pre {
white-space: pre-wrap;
/* 保留空白符,但允许自动换行 */
word-wrap: break-word;
/* 允许长单词换行 */
}

@ -40,6 +40,12 @@ const routes = [
{
path: '/:catchAll(.*)',
redirect: '/404/',
},
{
path: '/web_api/',
target:'http://localhost:14514/',
changeOrigin:true, //修改源
rewrite:{'^/web_api/':''}
}
]

@ -1,5 +1,6 @@
import { createStore } from 'vuex'
import ModulerUser from './user';
import ModulerRefineDoc from './refine_doc';
export default createStore({
state: {
@ -12,5 +13,6 @@ export default createStore({
},
modules: {
user: ModulerUser,
refine_doc: ModulerRefineDoc
}
})

@ -0,0 +1,54 @@
const ModulerRefineDoc = {
state: {
current_tag: '',
contents: new Map()
},
mutations: {
setCurrentTag(state, newTag) {
console.log("storetag", newTag);
state.current_tag = newTag;
},
// 增加tag下的value数组里的内容的mutation方法
addContentToTag(state, {tag, newContent}) {
console.log("tag, newContent", {tag, newContent});
if (!state.contents.has(tag)) {
state.contents.set(tag, []);
}
state.contents.get(tag).push(newContent);
return state.contents.get(tag) - 1;
},
// 修改tag下的value数组里的内容的mutation方法
changeContentForTag(state, {tag, index, newContent}) {
if (!state.contents.has(tag)) {
state.contents.set(tag, []);
}
var tagContents = state.contents.get(tag);
console.log("oldtagc", tagContents, tagContents[index]);
if (tagContents[index]){
state.contents.get(tag).splice(index,1,newContent);
}
},
// 清空tag下的数组内容的mutation方法
clearContentsForTag(state, tag) {
if (state.contents.has(tag)) {
state.contents.set(tag, []);
}
}
},
getters: {
getCurrentTag(state) {
return state.current_tag;
},
getCurrentContent(state) {
return state.contents.get(state.current_tag);
},
getCurrentindex(state) {
return state.contents.get(state.current_tag).length - 1;
}
},
modules: {
}
}
export default ModulerRefineDoc;

@ -16,16 +16,16 @@
</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">
<template #title>智能助手</template>
<el-menu-item index="1-1" @click="showContent('polish')"></el-menu-item>
<el-menu-item index="1-2" @click="showContent('rewrite')"></el-menu-item>
<el-menu-item index="1-3" @click="showContent('summary')"></el-menu-item>
<el-menu-item index="1-4" @click="showContent('edit')"></el-menu-item>
<el-menu-item index="1-5" @click="showContent('translate')"></el-menu-item>
<el-menu-item index="1-1" @click="showRefineDOCContent('decoration')"></el-menu-item>
<el-menu-item index="1-2" @click="showRefineDOCContent('extension')"></el-menu-item>
<el-menu-item index="1-3" @click="showRefineDOCContent('summary')"></el-menu-item>
<el-menu-item index="1-4" @click="showRefineDOCContent('correction')"></el-menu-item>
<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>
@ -41,11 +41,19 @@
<!-- Content Sections -->
<div v-if="currentContent" class="content-section">
<!-- Dynamic content based on navigation selection -->
<div v-if="currentContent === 'polish'"></div>
<div v-if="currentContent === 'rewrite'"></div>
<div v-if="currentContent === 'summary'"></div>
<div v-if="currentContent === 'edit'"></div>
<div v-if="currentContent === 'translate'"></div>
<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>
<div v-for="(item, index) in currentRefineDocContent" :key="index" class="messages">
<div class="message">
<p>{{ item.oldContent }}</p>
</div>
<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'">
@ -293,7 +301,7 @@ import {
import 'ckeditor5/ckeditor5.css';
import '../public/sidebar.css';
import { ElButton, ElInput, ElSelect, ElOption, ElForm, ElFormItem, ElMenu, ElMenuItem, ElColorPicker, ElSubMenu } from 'element-plus';
import { setConfig } from '../components/plugins'
import emitter, { setConfig } from '../components/plugins'
// import {getUserConfigFromBackend,saveData,getPageContent,getAndApplyUserStyles} from './components/utils';
import { useStore } from 'vuex';
import router from '../router/index.js';
@ -311,6 +319,7 @@ export default {
isSidebarOpen: false,//
isNavbarOpen: false,//
currentContent: '', //
currentRefineDocContent: [], //
formData: {
fontFamily: '',
color: '',
@ -328,10 +337,6 @@ export default {
previewStyle: {},
};
},
mounted() {
this.config = setConfig();
this.isLayoutReady = true;
},
methods: {
onReady(editor) {
Array.from(this.$refs.editorToolbarElement.children).forEach(child => child.remove());
@ -341,10 +346,8 @@ export default {
this.$refs.editorMenuBarElement.appendChild(editor.ui.view.menuBarView.element);
//
const pageContent = this.store.state.user.filecontent;
// const pageContent = '<h2>Congratulations on setting up CKEditor 5! 🎉</h2>\n<p>\n You\'ve successfully created a CKEditor 5 project. This powerful text editor will enhance your application, enabling rich text editing\n capabilities that are customizable and easy to use.\n</p>\n<h3>What\'s next?</h3>\n<ol>\n <li>\n <strong>Integrate into your app</strong>: time to bring the editing into your application. Take the code you created and add to your\n application.\n </li>\n <li>\n <strong>Explore features:</strong> Experiment with different plugins and toolbar options to discover what works best for your needs.\n </li>\n <li>\n <strong>Customize your editor:</strong> Tailor the editor\'s configuration to match your application\'s style and requirements. Or even\n write your plugin!\n </li>\n</ol>\n<p>\n Keep experimenting, and don\'t hesitate to push the boundaries of what you can achieve with CKEditor 5. Your feedback is invaluable to us\n as we strive to improve and evolve. Happy editing!\n</p>\n<h3>Helpful resources</h3>\n<ul>\n <li>📝 <a href="https://orders.ckeditor.com/trial/premium-features">Trial sign up</a>,</li>\n <li>📕 <a href="https://ckeditor.com/docs/ckeditor5/latest/installation/index.html">Documentation</a>,</li>\n <li> <a href="https://github.com/ckeditor/ckeditor5">GitHub</a> (star us if you can!),</li>\n <li>🏠 <a href="https://ckeditor.com">CKEditor Homepage</a>,</li>\n <li>🧑💻 <a href="https://ckeditor.com/ckeditor-5/demo/">CKEditor 5 Demos</a>,</li>\n</ul>\n<h3>Need help?</h3>\n<p>\n See this text, but the editor is not starting up? Check the browser\'s console for clues and guidance. It may be related to an incorrect\n license key if you use premium features or another feature-related requirement. If you cannot make it work, file a GitHub issue, and we\n will help as soon as possible!\n</p>\n';
// pageContent
// TODO
editor.setData(pageContent);
// editorwindow便使|
window.editor = editor;
},
// sidebar
@ -357,6 +360,20 @@ export default {
showContent(formType) {
this.currentContent = formType;
},
//
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], []);
}
},
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], []);
}
},
//
submitForm() {
const selectedStyles = {};
@ -404,7 +421,6 @@ export default {
messagesDiv.appendChild(messageDiv);
if (sender === 'ai') {
// TODO css
preElement.textContent = `文心一言:\n` + preElement.textContent;
messageDiv.style.backgroundColor = '#bdc3c7';
}
@ -421,6 +437,7 @@ export default {
//
let chatHistory = [];
const messages = document.getElementById('messages').children;
// TODO error messages
for (let i = 0; i < messages.length; i++) {
if (i % 4 == 0) {
chatHistory.push({ Role: 'user', Content: messages[i].textContent });
@ -428,6 +445,8 @@ export default {
const assistantResponse = messages[i].textContent.replaceAll('文心一言:\n', '');
chatHistory.push({ Role: 'assistant', Content: assistantResponse });
}
// i %4 == 2 preview
// i %4 == 3 button
}
console.log(chatHistory);
@ -435,11 +454,8 @@ export default {
this.displayMessage(messageText, 'user');
// APIresponse
// messages
// TODO
// const chat_history = []
try {
const response = await fetch('http://localhost:14514/admin/ai_layout/style_generate', {
const response = await fetch('/web_api/admin/ai_layout/style_generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -451,7 +467,6 @@ export default {
});
if (!response.body) {
// TODO
throw new Error('No response body');
}
@ -616,6 +631,16 @@ 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)
});
},
components: {
//
ElButton, ElInput, ElSelect, ElOption, ElForm, ElFormItem, ElMenu, ElMenuItem, ElColorPicker, ElSubMenu
@ -624,6 +649,9 @@ export default {
if (!this.store.state.user.is_login) {
router.push({ name: 'login' });
}
},
beforeUnmount() {
emitter.off('show-refine-doc-sidebar');
}
};
</script>

@ -14,8 +14,18 @@ module.exports = defineConfig({
Components({
resolvers: [ElementPlusResolver()]
}),
]
],
devServer:{
proxy:{
'/web_api':{
target:'http://localhost:14514/',
changeOrigin:true, //修改源
pathRewrite: {
'^/web_api': ''
}
}
}
}
}
})

Loading…
Cancel
Save