Skip to content

Commit

Permalink
feat: New graphic dialog function
Browse files Browse the repository at this point in the history
  • Loading branch information
Fleurxxx committed Sep 7, 2024
1 parent cfb6489 commit eb1493a
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 26 deletions.
227 changes: 202 additions & 25 deletions packages/plugins/robot/src/Main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<tiny-dropdown trigger="click" :show-icon="false">
<span>
<span>{{ selectedModel.label }}</span>
<icon-chevron-down class="ml8"></icon-chevron-down>
<icon-chevron-down class="ml8 arrow-down"></icon-chevron-down>
</span>
<template #dropdown>
<tiny-dropdown-menu popper-class="chat-model-popover" placement="bottom" :visible-arrow="false">
Expand All @@ -37,15 +37,16 @@
:key="index"
:flex="true"
:order="item.role === 'user' ? 'des' : 'asc'"
:justify="item.role === 'user' ? 'end' : 'start'"
:justify="item.role === 'assistant' ? 'start' : 'end'"
class="chat-message-row"
>
<tiny-col :span="1" :no="1" class="chat-avatar-wrap">
<tiny-col v-if="item.role !== 'system'" :span="1" :no="1" class="chat-avatar-wrap">
<img v-if="item.role !== 'user'" class="chat-avatar chat-avatar-ai" src="../assets/AI.png" />
<img v-else class="chat-avatar" :src="avatarUrl" />
</tiny-col>
<tiny-col :span="22" :no="2">
<div
v-if="item.role !== 'system'"
:class="[
'chat-content',
chatWindowOpened ? '' : 'hidden-text',
Expand All @@ -56,7 +57,10 @@
: 'chat-content-ai'
]"
>
<dialog-content :markdownContent="item.content" />
<dialog-content :markdown-content="item.content" />
</div>
<div v-else class="chat-message-image">
<img class="image" :src="item.content" alt="" />
</div>
</tiny-col>
</tiny-row>
Expand All @@ -72,6 +76,7 @@
<svg-icon name="chat-message" class="common-svg"></svg-icon>
</template>
<template #suffix>
<icon-picture class="common-svg upload-image" @click="openFilePicker"></icon-picture>
<svg-icon
name="chat-microphone"
:class="['common-svg', 'microphone', { 'microphone-svg': speechStatus }]"
Expand All @@ -82,6 +87,11 @@
<tiny-button @click="endContent">重新发起会话</tiny-button>
<tiny-button @click="sendContent(inputContent, false)">发送</tiny-button>
</footer>
<input type="file" ref="fileInput" style="display: none" @change="handleFileChange" />
<div class="preview-image" v-if="imageUrl !== ''" :data-animated="imageDeleting ? 'out' : ''">
<img class="image" :src="imageUrl" alt="" />
<icon-error class="delete-image" @click="handleDelete"></icon-error>
</div>
</div>
<token-dialog
:dialog-visible="tokenDialogVisible"
Expand All @@ -92,7 +102,7 @@
</template>

<script>
import { ref, onMounted, watch, watchEffect } from 'vue'
import { ref, onMounted, watch, unref, watchEffect } from 'vue'
import {
Layout,
Row,
Expand All @@ -106,7 +116,7 @@ import {
DropdownItem as TinyDropdownItem
} from '@opentiny/vue'
import { useCanvas, useHistory, usePage, useModal } from '@opentiny/tiny-engine-controller'
import { iconChevronDown, iconSetting } from '@opentiny/vue-icon'
import { iconChevronDown, iconSetting, iconPicture, iconError } from '@opentiny/vue-icon'
import { extend } from '@opentiny/vue-renderless/common/object'
import { useHttp } from '@opentiny/tiny-engine-http'
import { getBlockContent, initBlockList, AIModelOptions } from './js/robotSetting'
Expand All @@ -124,8 +134,10 @@ export default {
TinyDropdown,
TinyDropdownMenu,
TinyDropdownItem,
IconPicture: iconPicture(),
IconSetting: iconSetting(),
IconChevronDown: iconChevronDown(),
IconError: iconError(),
DialogContent,
TokenDialog
},
Expand Down Expand Up @@ -169,6 +181,8 @@ export default {
)
}
// TODO:返回schema格式的代码
// eslint-disable-next-line no-unused-vars
const createNewPage = (schema) => {
if (!(pageSettingState.isNew && pageSettingState.isAIPage)) {
pageSettingState.isNew = true
Expand All @@ -190,15 +204,14 @@ export default {
}
const codeRules = `
请扮演一名前端开发专家,生成代码时遵从以下几条要求:
###
1. 只使用element-ui组件库完成代码编写
2. 使用vue2技术栈
3. 回复中只能有一个代码块
4. el-table标签内不得出现el-table-column
###
`
我想让你充当 Stackoverflow 的帖子。我将提出与编程有关的问题,你将回答答案是什么。我希望你只回答给定的答案,在没有足够的细节时写出解释。
每次回复请遵循以下准则:
1. 如果需要展示代码,确保回复中只包含一个代码块。
2. 所有代码必须基于 Vue 3 框架编写。
3. 所有使用的组件必须来自 TinyVue 组件库,严禁使用 Element UI 等其他第三方组件库或原生组件。例如,想使用输入框组件,应该使用TinyVue组件库中的 \`tiny-input\`;想使用按钮组件,应该使用 TinyVue 组件库中的 \`tiny-button\`
4. 仔细阅读并遵循 [TinyVue 组件库文档](https://opentiny.design/tiny-vue/zh-CN/os-theme/overview) 中的指导,确保代码的准确性和一致性。
请根据上述准则,使用 TinyVue 组件库生成高质量的前端代码。
`
// 在每一次发送请求之前,都把引入区块的内容,给放到第一条消息中
// 为了不污染存储在localstorage里的用户的原始消息,这里进行了简单的对象拷贝
// 引入区块不存放在localstorage的原因:因为区块是可以变化的,用户可能在同一个会话中,对区块进行了删除和创建。那么存放的数据就不是即时数据了。
Expand All @@ -225,7 +238,7 @@ export default {
http
.post('/app-center/api/ai/chat', getSendSeesionProcess(), { timeout: 600000 })
.then((res) => {
const { originalResponse, schema } = res
const { originalResponse } = res
const responseMessage = getAiRespMessage(
originalResponse.choices?.[0]?.message.role,
originalResponse.choices?.[0]?.message.content
Expand All @@ -238,9 +251,10 @@ export default {
sessionProcess.displayMessages.push(respDisplayMessage)
messages.value[messages.value.length - 1].content = originalResponse.choices?.[0]?.message.content
setContextSession()
if (schema?.schema) {
createNewPage(schema.schema)
}
// TODO:返回schema格式的代码
// if (schema?.schema) {
// createNewPage(schema.schema)
// }
inProcesing.value = false
connectedFailed.value = false
})
Expand Down Expand Up @@ -284,8 +298,85 @@ export default {
tokenDialogVisible.value = true
}
const getMessage = (content) => ({
role: 'user',
/*
文件上传(仅支持图片,后续根据需求可添加上传类型)
*/
const fileInput = ref(null)
const openFilePicker = () => {
if (unref(fileInput)) {
unref(fileInput)?.click()
}
}
const imageUrl = ref('')
const imageContent = ref()
const uploadFile = (file) => {
const formData = new FormData()
const foundationModelData = JSON.stringify({
foundationModel: {
manufacturer: currentModel.manufacturer,
model: currentModel.value,
token: localStorage.getItem(currentModel.modelKey)
}
})
formData.append('foundationModel', foundationModelData)
formData.append('file', file)
http
.post('/app-center/api/ai/files', formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
timeout: 600000
})
.then((res) => {
imageContent.value = res.originalResponse
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
imageUrl.value = reader.result
}
})
.catch(() => {
Notify({
type: 'error',
message: '上传图片失败',
position: 'top-right',
duration: 5000
})
})
}
const handleFileChange = (event) => {
const files = event.target.files
if (!files.length) {
return
}
const file = files[0]
const validImageTypes = ['image/jpeg', 'image/png', 'image/jpg']
if (!validImageTypes.includes(file.type)) {
alert('请上传有效的图片文件(.jpeg, .png, .jpg)!')
event.target.value = ''
return
}
event.target.value = ''
uploadFile(file)
}
const imageDeleting = ref(false)
const handleDelete = () => {
imageDeleting.value = true
setTimeout(() => {
imageUrl.value = ''
imageContent.value = ''
imageDeleting.value = false
if (unref(fileInput)) {
unref(fileInput).value = ''
}
}, 500)
}
const getMessage = (content, role) => ({
role,
content,
name: 'John'
})
Expand All @@ -309,19 +400,24 @@ export default {
})
return
}
const realContent = content.trim()
const realContent = String(content).trim()
if (realContent) {
if (chatWindowOpened.value === false) {
await resizeChatWindow()
}
const message = getMessage(realContent)
const message = getMessage(realContent, 'user')
inProcesing.value = true
messages.value.push(message)
sessionProcess?.messages.push(message)
sessionProcess?.displayMessages.push(message)
if (imageContent.value) {
messages.value.push(getMessage(imageUrl.value, 'system'))
sessionProcess?.messages.push(getMessage(JSON.stringify(imageContent.value), 'system'))
sessionProcess?.displayMessages.push(getMessage(imageUrl.value, 'system'))
}
if (!isModel) {
inputContent.value = ''
imageUrl.value = ''
}
await scrollContent()
await sleep(1000)
Expand All @@ -331,7 +427,7 @@ export default {
}
}
// 根据localstorage初始化AI大模型
// 根据localstorage初始化AI大模型s
const initCurrentModel = (aiSession) => {
const currentModelValue = JSON.parse(aiSession)?.foundationModel?.model
currentModel = AIModelOptions.find((item) => item.value === currentModelValue)
Expand Down Expand Up @@ -390,6 +486,7 @@ export default {
message: '切换AI大模型将导致当前会话被清空,重新开启新会话,是否继续?',
exec() {
selectedModel.value = model
currentModel = model
endContent()
}
})
Expand Down Expand Up @@ -434,6 +531,12 @@ export default {
endContent,
resizeChatWindow,
setToken,
openFilePicker,
handleFileChange,
imageDeleting,
handleDelete,
fileInput,
imageUrl,
AIModelOptions,
selectedModel,
currentModel,
Expand All @@ -450,6 +553,70 @@ export default {
.common-svg {
color: var(--ti-lowcode-chat-model-common-icon);
}
.chat-message-image {
margin-right: 45px;
margin-top: -10px;
max-width: 100%;
border-radius: 5px;
border: 1px solid var(--ti-lowcode-chat-model-user-text-border);
.image {
width: 100px;
height: 70px;
border-radius: 5px;
}
}
.preview-image {
position: fixed;
bottom: 5px;
transform: translate(-50%, -50%);
z-index: 1000;
border-radius: 5px;
max-width: 100%;
height: auto;
animation: slideDown 0.5s ease-out forwards;
border: 1px solid var(--ti-lowcode-chat-model-user-text-border);
.image {
border-radius: 5px;
width: 100px;
height: 60px;
display: block;
position: relative;
border: #1a1a1a;
}
.delete-image {
color: red;
position: absolute;
top: -4px;
right: -3px;
cursor: pointer;
z-index: 1001;
}
}
.preview-image[data-animated='out'] {
animation: slideUp 0.5s ease-out forwards;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-100%);
}
}
.chat-title-icons {
font-size: 16px;
Expand All @@ -468,6 +635,13 @@ export default {
font-size: 14px;
margin-bottom: 20px;
color: var(--ti-lowcode-chat-model-title);
.arrow-down {
margin-left: 5px;
}
}
.tiny-dropdown .tiny-dropdown__trigger:not(.tiny-button) .tiny-svg {
vertical-align: middle;
}
.chat-window {
max-height: 400px;
Expand Down Expand Up @@ -549,6 +723,9 @@ export default {
font-size: 16px;
color: var(--ti-lowcode-chat-model-input-icon);
}
.upload-image {
margin-right: 7px;
}
.microphone {
font-size: 18px;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/robot/src/js/robotSetting.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const { getBlockList } = useBlock()
export const AIModelOptions = [
// 暂时不能使用,预留模型信息
// { label: 'ChatGPT:gpt-3.5-turbo', value: 'gpt-3.5-turbo', manufacturer: 'openai', modelKey: '' },
{ label: '文心一言:ERNIE-Bot-turbo', value: 'ERNIE-Bot-turbo', manufacturer: 'baiduai', modelKey: 'ACCESS_TOKEN' },
// { label: '文心一言:ERNIE-Bot-turbo', value: 'ERNIE-Bot-turbo', manufacturer: 'baiduai', modelKey: 'ACCESS_TOKEN' },
{ label: 'Kimi:moonshot-v1-8k', value: 'moonshot-v1-8k', manufacturer: 'kimi', modelKey: 'MOONSHOT_API_KEY' }
]

Expand Down

0 comments on commit eb1493a

Please sign in to comment.