Compare commits

..

5 Commits

@ -12,9 +12,20 @@ type StyleGenerate struct {
// ai生成样式 // ai生成样式
func (s *StyleGenerate) StyleGenerate(c *gin.Context) { func (s *StyleGenerate) StyleGenerate(c *gin.Context) {
if res, err := ai_model_cli.RequestStyle(c); err==nil { // 非流式传输
response.Success(c, consts.CurdStatusOkMsg, res.(string)) // if res, err := ai_model_cli.RequestStyle(c); err==nil {
} else { // response.Success(c, consts.CurdStatusOkMsg, res.(string))
// } else {
// response.Fail(c, consts.StyleGenerateFailCode, consts.StyleGenerateFailMsg, err)
// }
// 设置 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.RequestStyleStream(c); err != nil {
response.Fail(c, consts.StyleGenerateFailCode, consts.StyleGenerateFailMsg, err) response.Fail(c, consts.StyleGenerateFailCode, consts.StyleGenerateFailMsg, err)
} }
} }

@ -15,7 +15,7 @@ import (
func RequestStyle(c *gin.Context) (interface{}, error) { func RequestStyle(c *gin.Context) (interface{}, error) {
// userMsg := c.PostForm("user_input") // userMsg := c.PostForm("user_input")
userMsg:=c.GetString(consts.ValidatorPrefix+"user_input") userMsg := c.GetString(consts.ValidatorPrefix + "user_input")
qianfan.GetConfig().AccessKey = variable.ConfigYml.GetString("BaiduCE.QianFanAccessKey") qianfan.GetConfig().AccessKey = variable.ConfigYml.GetString("BaiduCE.QianFanAccessKey")
qianfan.GetConfig().SecretKey = variable.ConfigYml.GetString("BaiduCE.QianFanSecretKey") qianfan.GetConfig().SecretKey = variable.ConfigYml.GetString("BaiduCE.QianFanSecretKey")
@ -29,31 +29,52 @@ func RequestStyle(c *gin.Context) (interface{}, error) {
// 读取prompt文件 // 读取prompt文件
systemMsgPath := variable.ConfigYml.GetString("BaiduCE.StyleGeneratePromptPath") systemMsgPath := variable.ConfigYml.GetString("BaiduCE.StyleGeneratePromptPath")
// 读取文件内容 // 读取文件内容
prompt, err := os.ReadFile(variable.BasePath+systemMsgPath) prompt, err := os.ReadFile(variable.BasePath + systemMsgPath)
if err != nil || len(prompt) == 0 { if err != nil || len(prompt) == 0 {
variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err)) variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err))
return nil, err return nil, err
} }
// add user history to chat history // add user history to chat history
userHistory,exist := c.Get(consts.ValidatorPrefix+"chat_history") userHistory, exist := c.Get(consts.ValidatorPrefix + "chat_history")
if exist&&userHistory!=nil{ if exist && userHistory != nil {
// TODO: check if userHistory is of type []struct{Role string;Content string} // check if userHistory is of type []struct{Role string;Content string}
userHistory := userHistory.([]struct{Role string;Content string}) historySlice, ok := userHistory.([]interface{})
if len(userHistory)%2!=0{ if !ok || len(historySlice)%2 != 0 {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v", userHistory)) variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v", userHistory))
return nil, fmt.Errorf("用户历史对话格式错误") return nil, fmt.Errorf("用户历史对话格式错误")
} }
for _,msg := range userHistory{
chatHistory = append(chatHistory, qianfan.ChatCompletionMessage{Role:msg.Role,Content:msg.Content}) // convert userHistory to []qianfan.ChatCompletionMessage
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 nil, fmt.Errorf("用户历史对话格式错误")
}
} else {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\n无法将 item 转换为 map[string]interface{}", userHistory))
return nil, fmt.Errorf("用户历史对话格式错误")
}
}
if len(chatHistoryConverted) > 0 && len(chatHistoryConverted)%2 == 0 {
chatHistory = append(chatHistory, chatHistoryConverted...)
} }
} }
// add user input to chat history // add user input to chat history
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage(userMsg)) chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage(userMsg))
// define a stream chat client response, err := chat.Do(context.TODO(), &qianfan.ChatCompletionRequest{System: string(prompt), Messages: chatHistory})
response,err:=chat.Do(context.TODO(),&qianfan.ChatCompletionRequest{System: string(prompt),Messages: chatHistory})
if err != nil { if err != nil {
variable.ZapLog.Error(fmt.Sprintf("对话失败: %v", err)) variable.ZapLog.Error(fmt.Sprintf("对话失败: %v", err))
return nil, err return nil, err
@ -61,3 +82,87 @@ func RequestStyle(c *gin.Context) (interface{}, error) {
return response.Result, nil return response.Result, nil
} }
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()
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
}
// 将结果写入到响应体
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
}

@ -148,6 +148,9 @@ Captcha:
BaiduCE: BaiduCE:
ApiKey: "AR1SUIjaKSsCcDjj11QzHDOc" # 生成鉴权签名时使用的 API_KEY ApiKey: "AR1SUIjaKSsCcDjj11QzHDOc" # 生成鉴权签名时使用的 API_KEY
SecretKey: "zvEb5CzpuGCZNdQC1TPmDh3IOWn5aWDT" # 生成鉴权签名时使用的 SECRET_KEY SecretKey: "zvEb5CzpuGCZNdQC1TPmDh3IOWn5aWDT" # 生成鉴权签名时使用的 SECRET_KEY
QianFanAccessKey: "ALTAKOxb5YvHncyFr7Qbuv1cK0" # 访问千帆sdk 时用的 AccessKey # QianFanAccessKey: "ALTAKOxb5YvHncyFr7Qbuv1cK0" # 访问千帆sdk 时用的 AccessKey
QianFanSecretKey: "1edf17c358574e75b9913ebff7d95b61" # 访问千帆sdk 时用的 SecretKey
QianFanAccessKey: "ALTAK0utWNCwEoQtGHvHYf46yj"
# QianFanSecretKey: "1edf17c358574e75b9913ebff7d95b61" # 访问千帆sdk 时用的 SecretKey
QianFanSecretKey: "cb812e1b6e56420ea858d160e1351869"
StyleGeneratePromptPath: "/storage/app/prompt/style_generate.prompt" # 生成样式的提示词保存路径 StyleGeneratePromptPath: "/storage/app/prompt/style_generate.prompt" # 生成样式的提示词保存路径

@ -1,11 +1,11 @@
### 角色 ### 角色
你是一个设计和 CSS 专家,能够根据用户需求生成精确的 CSS 样式。 如果用户提出与生成CSS样式无关的问题请回答“对不起我无法回答该问题” 你是一个设计和 CSS 专家,能够根据用户需求生成精确的 CSS 样式。只能使用css来生成对应的样式不能修改页面的html代码和使用js代码。 如果用户提出与生成CSS样式无关的问题请回答“对不起我无法回答该问题”
### 任务 ### 知识
为以下元素生成 CSS 样式类 不同的样式对应的element如下
标题:<hi>i为标题级别如 h1, h2 等) 标题:<hi>i为标题级别如 h1, h2 等)
文本块:<p> 文本块:<p>
块引用:<blockquote> 块引用:<blockquote>
文本:<span> 文本:<span>
代码块:<pre> 代码块:<pre>
所有样式都需在 .ck-content 中定义,格式为: 所有样式都需在 .ck-content 中定义,格式为:
``` css ``` css
@ -14,8 +14,11 @@
[element为不同样式类对应的元素名如文本框对应的element为p] [element为不同样式类对应的元素名如文本框对应的element为p]
### 输出 ### 输出
仅输出你编写的 CSS 内容,不要对其进行解释和输出其他内容。!important 仅输出你编写的 CSS 内容,不要对其进行解释和输出其他内容。!important
只需要精确输出用户需要生成的样式,不要生成用户未指定的样式。
### 示例 ### 示例
生成一个文本块样式,生成的样式如下所示。 #### 示例一
用户输入:生成一个文本块(文本框)样式
输出:
``` css ``` css
.ck-content p.info-box { .ck-content p.info-box {
--background-size: 30px; --background-size: 30px;
@ -28,7 +31,10 @@
box-shadow: 5px 5px 0 #ffe6ef; box-shadow: 5px 5px 0 #ffe6ef;
} }
``` ```
生成一个代码框样式,生成的样式如下所示:
#### 示例二
用户输入:生成一个代码框样式
输出:
```css ```css
.ck-content pre.fancy-code { .ck-content pre.fancy-code {
border: 0; border: 0;
@ -52,7 +58,10 @@
box-shadow: 5px 5px 0 #0000001f; box-shadow: 5px 5px 0 #0000001f;
} }
``` ```
生成一个块引用样式,生成的样式如下所示
#### 示例三
用户输入:生成一个块引用样式
输出:
```css ```css
.ck-content blockquote.side-quote { .ck-content blockquote.side-quote {
font-family: 'Oswald'; font-family: 'Oswald';

@ -2,11 +2,7 @@ import { createApp } from 'vue';
import '../public/style.css'; import '../public/style.css';
import App from './App.vue'; import App from './App.vue';
import { CkeditorPlugin } from '@ckeditor/ckeditor5-vue'; import { CkeditorPlugin } from '@ckeditor/ckeditor5-vue';
import mitt from 'mitt';
const app = createApp(App); const app = createApp(App);
const emitter = mitt();
app.config.globalProperties.emitter = emitter;
app.use(CkeditorPlugin); app.use(CkeditorPlugin);
app.mount('#app'); app.mount('#app');

@ -5,9 +5,7 @@
<div class="file-item-icon" > <div class="file-item-icon" >
<img src="@/assets/file_icon.png" alt=""> <img src="@/assets/file_icon.png" alt="">
</div> </div>
<FileTitle> <FileTitle @leftclick="leftclick" @changetitlestate="changetitlestate" @changetitle="changetitle" :item="item" />
{{ item.name }}
</FileTitle>
</div> </div>
</template> </template>
@ -45,10 +43,24 @@ export default {
const doubleclick = () => { const doubleclick = () => {
context.emit('open_file'); context.emit('open_file');
} }
const changetitlestate = () => {
context.emit('changetitle',props.item);
}
const changetitle = (newname) => {
context.emit('changetitle',{
item:props.item,
newname,
});
}
return { return {
leftclick, leftclick,
rightclick, rightclick,
doubleclick, doubleclick,
changetitle,
changetitlestate,
} }
} }
} }
@ -69,16 +81,6 @@ export default {
height: 6vw; height: 6vw;
} }
.file-item-title{
width: 6vw;
height: 4vw;
text-align: center;
overflow: hidden;
font-size: 1.2vw;
word-break: break-all;
}
.file-item-selected { .file-item-selected {
border: 0.1vh solid #99d1ff; border: 0.1vh solid #99d1ff;
background: #cce8ff; background: #cce8ff;

@ -1,31 +1,51 @@
<template> <template>
<div class="file-item-title"> <div class="file-item-title" @click.stop="titleclick">
<slot></slot> <input v-if="item.editable" @click.stop="" @dblclick.stop="" type="text" v-model="filename"/>
<div v-else >
{{ filename }}
</div>
</div> </div>
</template> </template>
<script> <script>
import { computed, ref } from 'vue';
export default { export default {
name: 'FileTitle', name: 'FileTitle',
props:{
item:{
type:Object,
required:true,
}
},
setup(props,context){
let filename = ref(props.item.name);
let d = computed(() => {
if (!props.item.editable) {
context.emit("changetitle",filename.value);
}
return false;
});
const titleclick = () => {
if (props.item.is_selected) {
context.emit("changetitlestate");
}
else {
context.emit("leftclick");
}
}
return {
titleclick,
filename,
d,
}
}
} }
</script> </script>
<style scoped> <style scoped>
.file-item {
width: 8vw;
height: 8vw;
overflow: hidden;
display: inline-block;
margin: 0 0.1vw 1vw 0.1vw;
border: 0.1vh solid white;
}
.file-item-icon > img {
width: 6vw;
height: 6vw;
}
.file-item-title{ .file-item-title{
width: 6vw; width: 6vw;
@ -40,4 +60,11 @@ export default {
border: 0.1vh solid #99d1ff; border: 0.1vh solid #99d1ff;
background: #cce8ff; background: #cce8ff;
} }
input {
width: 6vw;
height: 1.8vw;
overflow: hidden;
font-size: 1.2vw;
}
</style> </style>

@ -5,9 +5,7 @@
<div class="file-item-icon" > <div class="file-item-icon" >
<img src="@/assets/folder_icon.png" alt=""> <img src="@/assets/folder_icon.png" alt="">
</div> </div>
<FileTitle> <FileTitle @leftclick="leftclick" @changetitle="changetitle" :item="item" />
{{ item.name }}
</FileTitle>
</div> </div>
</template> </template>
@ -49,10 +47,15 @@ export default {
context.emit('ls'); context.emit('ls');
} }
const changetitle = () => {
context.emit('changetitle',props.item);
}
return { return {
leftclick, leftclick,
rightclick, rightclick,
doubleclick, doubleclick,
changetitle,
} }
} }
} }

@ -38,7 +38,7 @@ import {
LinkImage, LinkImage,
List, List,
ListProperties, ListProperties,
Markdown, // Markdown,
MediaEmbed, MediaEmbed,
Mention, Mention,
PageBreak, PageBreak,
@ -82,7 +82,8 @@ import {
getStyle, getStyle,
getPageContent, getPageContent,
getUserConfigFromBackend, getUserConfigFromBackend,
saveData saveData,
// markdown2html
} from './utils'; } from './utils';
// 导出为docx插件 // 导出为docx插件
@ -418,7 +419,7 @@ function setConfig() {
LinkImage, LinkImage,
List, List,
ListProperties, ListProperties,
Markdown, // Markdown,
MediaEmbed, MediaEmbed,
Mention, Mention,
PageBreak, PageBreak,

@ -1,5 +1,5 @@
// utils.js // utils.js
import { MarkdownToHtml } from '@ckeditor/ckeditor5-markdown-gfm/src/markdown2html/markdown2html.js';
// 获取用户配置 // 获取用户配置
export function getUserConfigFromBackend() { export function getUserConfigFromBackend() {
// TODO 请求用户配置 // TODO 请求用户配置
@ -101,6 +101,7 @@ export function saveData(data) {
// resolve(); // resolve();
// }, HTTP_SERVER_LAG ); // }, HTTP_SERVER_LAG );
// } ); // } );
console.log('saving...');
console.log(data); console.log(data);
} }
@ -121,9 +122,10 @@ export function getStyle() {
// 获取用户编辑的内容 <html> // 获取用户编辑的内容 <html>
export function getPageContent() { export function getPageContent() {
const pageContent =document.querySelector("#app > div > div > div.editor-container.editor-container_document-editor.editor-container_include-style > div.editor-container__editor-wrapper > div > div"); return window.editor.getData();
// const pageContent =document.querySelector("#app > div > div > div.editor-container.editor-container_document-editor.editor-container_include-style > div.editor-container__editor-wrapper > div > div");
// const pageContent = document.querySelector("#app > div > div > div > div.editor-container__editor-wrapper > div > div > div"); // const pageContent = document.querySelector("#app > div > div > div > div.editor-container__editor-wrapper > div > div > div");
return pageContent.outerHTML; // return pageContent.outerHTML;
} }
// 获取并应用用户定义的样式 // 获取并应用用户定义的样式
@ -136,3 +138,12 @@ export function getAndApplyUserStyles() {
document.head.appendChild(styleElement); document.head.appendChild(styleElement);
return styles; return styles;
} }
// markdown转html 便于将大语言模型的输出一般为markdown格式转换为ckeditor的html格式
// 利用ckeditor markdown插件但不能在CkeditorView.vue中使用
// 否则会改变编辑器数据处理器为markdown即getData()需要传入markdown stringsetData()返回markdown string
export function markdown2html(markdownString){
const markdownToHtml = new MarkdownToHtml();
const htmlString = markdownToHtml.parse(markdownString);
return htmlString;
}

@ -3,7 +3,7 @@
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 350px; width: 450px;
height: 100%; height: 100%;
overflow-y: hidden; overflow-y: hidden;
transition: transform 0.3s ease; transition: transform 0.3s ease;

@ -1,4 +1,5 @@
import $ from 'jquery'; // import $ from 'jquery';
import axios from 'axios';
const ModulerUser = { const ModulerUser = {
state: { state: {
@ -37,38 +38,40 @@ const ModulerUser = {
}, },
actions: { actions: {
login(context,data){ login(context,data){
$.ajax({ axios({
url:'http://47.106.113.194:8000/token/', url:'http://1.94.171.222:8000/token/',
type:'POST', method:'POST',
data:{ data:{
username:data.username, username:data.username,
password:data.password, password:data.password,
}, },
success:resp => { })
const {access,refresh} = resp; .then(resp => {
$.ajax({ const {access,refresh} = resp.data;
url:' http://47.106.113.194:8000/getinfo/', axios({
type:'GET', url:'http://1.94.171.222:8000/getinfo/',
method:'GET',
headers:{ headers:{
'Authorization': "Bearer " + access, 'Authorization': "Bearer " + access,
}, },
success(resp){ })
.then(resp => {
context.commit('updateUser',{ context.commit('updateUser',{
...resp, username:resp.data.username,
access:access, access:access,
refresh:refresh, refresh:refresh,
is_login: true, is_login: true,
}); });
context.commit('forwardPath',"~/users/" + resp.username + '/' context.commit('forwardPath',resp.data.username + '/'
); );
data.success(); data.success();
} }
}); );
}, })
error(){ .catch(() => {
data.error(); data.error();
} }
}); );
} }
}, },
modules: { modules: {

@ -297,7 +297,6 @@ import { setConfig } from '../components/plugins'
// import {getUserConfigFromBackend,saveData,getPageContent,getAndApplyUserStyles} from './components/utils'; // import {getUserConfigFromBackend,saveData,getPageContent,getAndApplyUserStyles} from './components/utils';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import router from '../router/index.js'; import router from '../router/index.js';
import axios from 'axios';
export default { export default {
name: 'CkeditorView', name: 'CkeditorView',
@ -327,7 +326,6 @@ export default {
wordSpacing: '' wordSpacing: ''
}, },
previewStyle: {}, previewStyle: {},
messages: [], //
}; };
}, },
mounted() { mounted() {
@ -347,6 +345,7 @@ export default {
// pageContent // pageContent
// TODO // TODO
editor.setData(pageContent); editor.setData(pageContent);
window.editor = editor;
}, },
// sidebar // sidebar
// / // /
@ -395,10 +394,6 @@ export default {
displayMessage(text, sender) { displayMessage(text, sender) {
const messagesDiv = document.getElementById('messages'); const messagesDiv = document.getElementById('messages');
// messages
const previousButtonsMessage = messagesDiv.querySelectorAll('.preview-buttons');
previousButtonsMessage.forEach(buttons => buttons.remove());
const messageDiv = document.createElement('div'); const messageDiv = document.createElement('div');
messageDiv.className = 'message'; messageDiv.className = 'message';
// 使 pre text // 使 pre text
@ -412,28 +407,111 @@ export default {
// TODO css // TODO css
preElement.textContent = `文心一言:\n` + preElement.textContent; preElement.textContent = `文心一言:\n` + preElement.textContent;
messageDiv.style.backgroundColor = '#bdc3c7'; messageDiv.style.backgroundColor = '#bdc3c7';
}
// messagesDiv.scrollTop = messagesDiv.scrollHeight;
return preElement;
},
//
async sendMessage() {
const userInput = document.getElementById('userInput');
const messageText = userInput.value;
if (messageText.trim() === '') return;
//
let chatHistory = [];
const messages = document.getElementById('messages').children;
for (let i = 0; i < messages.length; i++) {
if (i % 4 == 0) {
chatHistory.push({ Role: 'user', Content: messages[i].textContent });
} else if (i % 4 == 1) {
const assistantResponse = messages[i].textContent.replaceAll('文心一言:\n', '');
chatHistory.push({ Role: 'assistant', Content: assistantResponse });
}
}
console.log(chatHistory);
// Display user's message
this.displayMessage(messageText, 'user');
// APIresponse
// messages
// TODO
// const chat_history = []
try {
const response = await fetch('http://localhost:14514/admin/ai_layout/style_generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_input: userInput.value,
...(chatHistory.length > 0 && { chat_history: chatHistory })
})
});
if (!response.body) {
// TODO
throw new Error('No response body');
}
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let result = '';
var messageStream;
/* eslint-disable no-constant-condition */
while (true) {
const { done, value } = await reader.read();
if (done) break;
const slice = decoder.decode(value, { stream: true });
result += slice;
if (!messageStream) {
messageStream = this.displayMessage(slice, 'ai');
} else {
messageStream.textContent += slice;
}
}
console.log('生成的样式:', result);
//
this.previewStyleAtMessages(result);
/* eslint-enable no-constant-condition */
} catch (error) {
console.error('Error:', error);
this.displayError(error);
}
//
userInput.value = '';
},
//
previewStyleAtMessages(style) {
const messagesDiv = document.getElementById('messages');
// preview // preview
const previewWrapper = document.createElement('div'); const previewWrapper = document.createElement('div');
previewWrapper.style.border = '1px solid #ccc'; previewWrapper.style.border = '1px solid #ccc';
previewWrapper.style.display = 'flow-root'; previewWrapper.style.display = 'flow-root';
messagesDiv.appendChild(previewWrapper);
// 'preview' // 'preview'
const previewLabel = document.createElement('div'); const previewLabel = document.createElement('div');
previewLabel.textContent = 'preview'; previewLabel.textContent = 'preview';
previewWrapper.appendChild(previewLabel); previewWrapper.appendChild(previewLabel);
// //
const previewStyle = document.createElement('style'); const previewStyle = document.createElement('style');
// //
// csscss,css,} // csscss,css,}
const cssRegex = /css([\s\S]*)\}/; const cssRegex = /```css\n([\s\S]*?)```/;
const cssMatch = cssRegex.exec(text); const cssMatch = cssRegex.exec(style);
let cssText; // css
// css
if (cssMatch) { if (cssMatch) {
previewStyle.textContent = cssMatch[0].replace('css', ''); cssText = cssMatch[0].replace('css', '').replaceAll('```', '');
} else { } else {
previewStyle.textContent = text; cssText = style;
} }
previewStyle.textContent = previewStyle.textContent.replace(/\.ck-content/g, ''); previewStyle.textContent = cssText.replace(/\.ck-content/g, '');
document.head.appendChild(previewStyle); previewWrapper.appendChild(previewStyle);
// //
let previewElement; let previewElement;
@ -441,18 +519,28 @@ export default {
const styleRegex = /\.ck-content\s+([a-z]+)\.([a-z-]+)\s*\{/g; const styleRegex = /\.ck-content\s+([a-z]+)\.([a-z-]+)\s*\{/g;
let match; let match;
const classNames = []; const classNames = [];
while ((match = styleRegex.exec(text)) !== null) { while ((match = styleRegex.exec(cssText)) !== null) {
if (!previewElement) { if (!previewElement) {
previewElement = document.createElement(match[1]); previewElement = document.createElement(match[1]);
if (match[1] === 'span') {
previewElement.textContent = 'AaBbCcDdEeFf'; previewElement.textContent = 'AaBbCcDdEeFf';
} else {
const previewText = document.createElement('p');
previewText.textContent = 'AaBbCcDd';
previewElement.appendChild(previewText);
}
previewWrapper.appendChild(previewElement); previewWrapper.appendChild(previewElement);
} }
classNames.push(match[2]);
// classNameclass
const newClassName = match[2] + "_" + Math.floor(Math.random() * 1000);
previewStyle.textContent = previewStyle.textContent.replaceAll(match[2], newClassName);
classNames.push(newClassName);
} }
if (previewElement) { if (previewElement) {
previewElement.className = classNames.join(' '); previewElement.className = classNames.join(' ');
} }
messagesDiv.appendChild(previewWrapper);
// //
const buttonsMessageDiv = document.createElement('div'); const buttonsMessageDiv = document.createElement('div');
@ -479,7 +567,39 @@ export default {
classes: classNames classes: classNames
}; };
console.log(styleDefinition); console.log(styleDefinition);
console.log(cssText);// css
}; };
//
buttonsMessageDiv.appendChild(saveButton);
buttonsMessageDiv.appendChild(this.createClearButton());
messagesDiv.appendChild(buttonsMessageDiv);
},
//
displayError(error) {
const messagesDiv = document.getElementById('messages');
const messageDiv = document.createElement('div');
messageDiv.className = 'message';
//
messageDiv.style.backgroundColor = 'mistyrose'; // mistyrose
// 使 pre text
// css htmlpre set language
const preElement = document.createElement('pre');
//
preElement.textContent = error;
preElement.style = "color:red";
messageDiv.appendChild(preElement);
messagesDiv.appendChild(messageDiv);
//
const buttonsMessageDiv = document.createElement('div');
buttonsMessageDiv.className = 'preview-buttons';
buttonsMessageDiv.style.display = 'flex';
buttonsMessageDiv.style.justifyContent = 'flex-end';
buttonsMessageDiv.style.marginTop = '10px';
buttonsMessageDiv.appendChild(this.createClearButton());
messagesDiv.appendChild(buttonsMessageDiv);
},
createClearButton() {
// clear // clear
const clearButton = document.createElement('el-button'); const clearButton = document.createElement('el-button');
clearButton.innerHTML = '<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>'; clearButton.innerHTML = '<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>';
@ -492,48 +612,9 @@ export default {
while (messagesDiv.firstChild) { while (messagesDiv.firstChild) {
messagesDiv.removeChild(messagesDiv.firstChild); messagesDiv.removeChild(messagesDiv.firstChild);
} }
// style
document.head.removeChild(previewStyle);
}; };
// return clearButton;
buttonsMessageDiv.appendChild(saveButton);
buttonsMessageDiv.appendChild(clearButton);
messagesDiv.appendChild(buttonsMessageDiv);
} }
// messagesDiv.scrollTop = messagesDiv.scrollHeight;
},
sendMessage() {
const userInput = document.getElementById('userInput');
const messageText = userInput.value;
if (messageText.trim() === '') return;
// Display user's message
this.displayMessage(messageText, 'user');
// APIresponse
// messages
// TODO
chat_history = []
axios({
url:'http://localhost:14514/admin/ai_layout/style_generate',
method: 'POST',
data:{
user_input: userInput.value
}
})
.then(response => {
// let formatedResponse = response.data.response;
// this.displayMessage(formatedResponse, 'ai');
console.log(response);
this.displayMessage(response.data.data,'ai')
})
.catch(error => {
console.error('Error:', error);
});
userInput.value = '';
},
}, },
components: { components: {
// //

@ -2,15 +2,14 @@
<PathBar @ls="ls" /> <PathBar @ls="ls" />
<ContentBase @click="leftclick" @contextmenu.prevent="rightclick($event)"> <ContentBase @click="leftclick" @contextmenu.prevent="rightclick($event)">
<div v-for="item in items" :key="item.name" class="file-item"> <div v-for="item in items" :key="item.name" class="file-item">
<FolderFiled @select_item='select_item' @filerightclick="filerightclick" @fileleftclick="fileleftclick" @ls="ls" v-if="item.type==='folder'" :item="item" /> <FolderFiled @changetitlestate="changetitlestate" @changetitle="changetitle" @select_item='select_item' @filerightclick="filerightclick" @fileleftclick="fileleftclick" @ls="ls" v-if="item.type==='folder'" :item="item" />
<FileFiled @select_item='select_item' @filerightclick="filerightclick" @fileleftclick="fileleftclick" @open_file='open_file' v-if="item.type==='file'" :item="item" /> <FileFiled @changetitlestate="changetitlestate" @changetitle="changetitle" @select_item='select_item' @filerightclick="filerightclick" @fileleftclick="fileleftclick" @open_file='open_file' v-if="item.type==='file'" :item="item" />
</div> </div>
<RightMenu v-if="menuvisible" @open_file="open_file" @turn_back="turn_back" :menutype="menutype" :menuposition="menuposition" /> <RightMenu v-if="menuvisible" @open_file="open_file" @turn_back="turn_back" :menutype="menutype" :menuposition="menuposition" />
</ContentBase> </ContentBase>
</template> </template>
<script> <script>
import $ from 'jquery';
import ContentBase from '../components/ContentBase'; import ContentBase from '../components/ContentBase';
import FileFiled from '../components/FileFiled'; import FileFiled from '../components/FileFiled';
import FolderFiled from '../components/FolderFiled'; import FolderFiled from '../components/FolderFiled';
@ -19,6 +18,7 @@ import PathBar from '../components/PathBar.vue';
import { ref } from 'vue'; import { ref } from 'vue';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import router from '@/router/index.js'; import router from '@/router/index.js';
import axios from 'axios';
export default { export default {
@ -44,27 +44,34 @@ export default {
const ls = () => { const ls = () => {
menuvisible.value = false; menuvisible.value = false;
$.ajax({ axios({
url:' http://47.106.113.194:8000/operation/command/ls/', url:' http://1.94.171.222:8000/operation/command/ls/',
type:'get', type:'get',
data:{ params:{
'path':store.state.user.path.join(""), 'path':store.state.user.path.join(""),
}, },
success:resp => { })
.then(resp => {
let tmp = []; let tmp = [];
for (let i = 0;i < resp.items.length;i++) { for (let i = 0;i < resp.data.items.length;i++) {
let item = resp.items[i]; let item = resp.data.items[i];
item.is_selected = false; item.is_selected = false;
item.editable = false;
tmp.push(item); tmp.push(item);
} }
items.value = tmp; items.value = tmp;
} }
}); );
} }
const select_item = (item) => { const select_item = (item) => {
console.log("select");
let tmp = items.value; let tmp = items.value;
for (let i = 0;i < tmp.length;i++){ for (let i = 0;i < tmp.length;i++){
if (tmp[i].editable){
//rename
tmp[i].editable = false;
}
if (tmp[i] === item){ if (tmp[i] === item){
tmp[i].is_selected = true; tmp[i].is_selected = true;
} }
@ -88,18 +95,18 @@ export default {
ls(); ls();
} }
else { else {
$.ajax({ axios({
url:' http://47.106.113.194:8000/operation/open/', url:' http://1.94.171.222:8000/operation/open/',
type:'get', type:'get',
data:{ params:{
'path':store.state.user.path.join("") + selected_item.name, 'path':store.state.user.path.join("") + selected_item.name,
}, },
success:resp => { })
store.commit('updateFileContent',resp.content); .then(resp => {
console.log(resp); store.commit('updateFileContent',resp.data.content);
router.push({name:'ckeditor'}); router.push({name:'ckeditor'});
} }
}); );
} }
} }
@ -141,6 +148,31 @@ export default {
select_item(data.item); select_item(data.item);
} }
const changetitlestate = (item) => {
item.editable = true;
}
const changetitle = (data) => {
data.item.editable = true;
rename(data.item,data.newname);
}
const rename = (item,newname) => {
axios({
url:' http://1.94.171.222:8000/operation/open/',
type:'get',
params:{
'path':store.state.user.path.join("") + item.name,
'newname':newname,
},
})
.then(resp => {
if (resp.data.result === 'success')
ls();
}
);
}
if (store.state.user.is_login){ if (store.state.user.is_login){
ls(); ls();
} }
@ -159,6 +191,8 @@ export default {
rightclick, rightclick,
open_file, open_file,
turn_back, turn_back,
changetitle,
changetitlestate,
menuvisible, menuvisible,
menuposition, menuposition,
menutype, menutype,

@ -37,7 +37,6 @@ export default {
let error_message = ref(''); let error_message = ref('');
const login = () => { const login = () => {
// http://47.106.113.194:8000/token/
store.dispatch('login',{ store.dispatch('login',{
username:username.value, username:username.value,
password:password.value, password:password.value,

Loading…
Cancel
Save