mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
Merge branch 'main' of github.com:maxkb-dev/maxkb
This commit is contained in:
commit
e1e050d153
File diff suppressed because it is too large
Load Diff
|
|
@ -15,12 +15,15 @@
|
|||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"element-plus": "^2.3.14",
|
||||
"install": "^0.13.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
"npm": "^10.2.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.6",
|
||||
"pinyin-pro": "^3.18.2",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4",
|
||||
"mitt": "^3.0.0"
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.3.2",
|
||||
|
|
|
|||
|
|
@ -7,4 +7,9 @@ interface ApplicationFormType {
|
|||
example?: string[]
|
||||
dataset_id_list: string[]
|
||||
}
|
||||
export type { ApplicationFormType }
|
||||
interface chatType {
|
||||
id: string
|
||||
problem_text: string
|
||||
answer_text: string
|
||||
}
|
||||
export type { ApplicationFormType, chatType }
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
<template>
|
||||
<div class="ai-dialog p-24">
|
||||
<el-scrollbar>
|
||||
<div class="ai-dialog__content">
|
||||
<el-scrollbar ref="scrollDiv">
|
||||
<div ref="dialogScrollbar" class="ai-dialog__content">
|
||||
<div class="item-content mb-16">
|
||||
<div class="avatar">
|
||||
<AppAvatar class="avatar-gradient">
|
||||
<img src="@/assets/icon_robot.svg" style="width: 54%" alt="" />
|
||||
</AppAvatar>
|
||||
<!-- <AppAvatar>
|
||||
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
|
||||
</AppAvatar> -->
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
|
@ -36,28 +33,35 @@
|
|||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-content mb-16">
|
||||
<div class="avatar">
|
||||
<AppAvatar>
|
||||
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
|
||||
</AppAvatar>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="text">
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
<template v-for="(item, index) in chatList" :key="index">
|
||||
<!-- 问题 -->
|
||||
<div class="item-content mb-16 lighter">
|
||||
<div class="avatar">
|
||||
<AppAvatar>
|
||||
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
|
||||
</AppAvatar>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="text">
|
||||
{{ item.problem_text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-content mb-16">
|
||||
<div class="avatar">
|
||||
<AppAvatar class="avatar-gradient">
|
||||
<img src="@/assets/icon_robot.svg" style="width: 54%" alt="" />
|
||||
</AppAvatar>
|
||||
<!-- 回答 -->
|
||||
<div class="item-content mb-16 lighter">
|
||||
<div class="avatar">
|
||||
<AppAvatar class="avatar-gradient">
|
||||
<img src="@/assets/icon_robot.svg" style="width: 54%" alt="" />
|
||||
</AppAvatar>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="flex" v-if="!item.answer_text">
|
||||
<el-card shadow="always" class="dialog-card"> {{ '回答中...' }} </el-card>
|
||||
</div>
|
||||
<el-card v-else shadow="always" class="dialog-card"> {{ item.answer_text }} </el-card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-card shadow="always" class="dialog-card"> XXXXXXXXX </el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="ai-dialog__operate p-24">
|
||||
|
|
@ -67,13 +71,15 @@
|
|||
type="textarea"
|
||||
placeholder="请输入"
|
||||
:autosize="{ minRows: 1, maxRows: 8 }"
|
||||
@keydown.enter="sendChatHandle($event)"
|
||||
:disabled="loading"
|
||||
/>
|
||||
<div class="operate" v-loading="loading">
|
||||
<el-button
|
||||
text
|
||||
class="sent-button"
|
||||
:disabled="!(inputValue && data?.name && data?.model_id)"
|
||||
@click="chatHandle"
|
||||
@click="sendChatHandle"
|
||||
>
|
||||
<img
|
||||
v-show="!(inputValue && data?.name && data?.model_id)"
|
||||
|
|
@ -85,7 +91,6 @@
|
|||
src="@/assets/icon_send_colorful.svg"
|
||||
alt=""
|
||||
/>
|
||||
<!-- <AppIcon iconName="app-send"></AppIcon> -->
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -93,24 +98,44 @@
|
|||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, nextTick, onUpdated } from 'vue'
|
||||
import applicationApi from '@/api/application'
|
||||
import type { chatType } from '@/api/type/application'
|
||||
import { randomId } from '@/utils/utils'
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
|
||||
const scrollDiv = ref()
|
||||
const dialogScrollbar = ref()
|
||||
const loading = ref(false)
|
||||
const inputValue = ref('')
|
||||
const chartOpenId = ref('')
|
||||
const chatList = ref<chatType[]>([])
|
||||
|
||||
function quickProblemHandel(val: string) {
|
||||
inputValue.value = val
|
||||
}
|
||||
|
||||
function sendChatHandle(event: any) {
|
||||
if (!event.ctrlKey) {
|
||||
// 如果没有按下组合键ctrl,则会阻止默认事件
|
||||
event.preventDefault()
|
||||
|
||||
chatMessage()
|
||||
} else {
|
||||
// 如果同时按下ctrl+回车键,则会换行
|
||||
inputValue.value += '\n'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话
|
||||
*/
|
||||
function chatHandle() {
|
||||
function getChartOpenId() {
|
||||
loading.value = true
|
||||
const obj = {
|
||||
model_id: props.data.model_id,
|
||||
|
|
@ -120,28 +145,56 @@ function chatHandle() {
|
|||
applicationApi
|
||||
.postChatOpen(obj)
|
||||
.then((res) => {
|
||||
chatMessage(res.data)
|
||||
chartOpenId.value = res.data
|
||||
chatMessage()
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function chatMessage(chatId: string) {
|
||||
applicationApi.postChatMessage(chatId, inputValue.value).then(async (response) => {
|
||||
const reader = response.body.getReader()
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
loading.value = false
|
||||
break
|
||||
function chatMessage() {
|
||||
loading.value = true
|
||||
if (!chartOpenId.value) {
|
||||
getChartOpenId()
|
||||
} else {
|
||||
applicationApi.postChatMessage(chartOpenId.value, inputValue.value).then(async (response) => {
|
||||
const randomNum = randomId()
|
||||
chatList.value.push({
|
||||
id: randomNum,
|
||||
problem_text: inputValue.value,
|
||||
answer_text: ''
|
||||
})
|
||||
inputValue.value = ''
|
||||
const reader = response.body.getReader()
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
loading.value = false
|
||||
break
|
||||
}
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
const str = decoder.decode(value, { stream: true })
|
||||
// console.log(JSON?.parse(str.replace('data:', '')))
|
||||
const content = JSON?.parse(str.replace('data:', ''))?.content
|
||||
if (content) {
|
||||
chatList.value[chatList.value.findIndex((v) => v.id === randomNum)].answer_text += content
|
||||
}
|
||||
}
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
const str = decoder.decode(value, { stream: true })
|
||||
console.log('value', JSON.parse(str.replace('data:', '')))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动到底部
|
||||
function handleScrollBottom() {
|
||||
nextTick(() => {
|
||||
scrollDiv.value.setScrollTop(dialogScrollbar.value.scrollHeight)
|
||||
})
|
||||
}
|
||||
|
||||
onUpdated(() => {
|
||||
handleScrollBottom()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ai-dialog {
|
||||
|
|
@ -153,6 +206,7 @@ function chatMessage(chatId: string) {
|
|||
position: relative;
|
||||
padding-right: 20px;
|
||||
padding-top: 0;
|
||||
color: var(--app-text-color);
|
||||
&__content {
|
||||
width: 99%;
|
||||
padding-bottom: 96px;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,62 @@
|
|||
<template>
|
||||
<el-avatar :size="30" v-bind="$attrs">
|
||||
<el-avatar
|
||||
:size="30"
|
||||
:style="{ background: props.pinyinColor && getAvatarColour(firstUserName) }"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot> {{ firstUserName }} </slot>
|
||||
</el-avatar>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { pinyin } from 'pinyin-pro';
|
||||
import { computed } from 'vue'
|
||||
defineOptions({ name: 'AppAvatar' })
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
pinyinColor: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const firstUserName = computed(() => {
|
||||
return props.name?.substring(0, 1)
|
||||
})
|
||||
|
||||
function getAvatarColour(name: string) {
|
||||
const charIndex = pinyin.getFullChars(name).charAt(0).toUpperCase().charCodeAt(0) - 65
|
||||
const colours = [
|
||||
'#ACA9E5',
|
||||
'#BCC934',
|
||||
'#B3CFE8',
|
||||
'#DCDEB5',
|
||||
'#D65A4A',
|
||||
'#E0C78B',
|
||||
'#E59191',
|
||||
'#E99334',
|
||||
'#FF6632',
|
||||
'#F4B7EF',
|
||||
'#F7D407',
|
||||
'#F8BB98',
|
||||
'#2BCBB1',
|
||||
'#3594F1',
|
||||
'#486660',
|
||||
'#4B689F',
|
||||
'#5976F6',
|
||||
'#72B1B2',
|
||||
'#778293',
|
||||
'#7D6624',
|
||||
'#82CBB5',
|
||||
'#837F6A',
|
||||
'#87B087',
|
||||
'#9AC0C4',
|
||||
'#958E55',
|
||||
'#99E4F2'
|
||||
]
|
||||
return colours[charIndex]
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ instance.interceptors.request.use(
|
|||
//设置响应拦截器
|
||||
instance.interceptors.response.use(
|
||||
(response: any) => {
|
||||
console.log('instance_response', response)
|
||||
if (response.data) {
|
||||
if (response.status !== 200 && !(response.data instanceof Blob)) {
|
||||
MsgError(response.data.message)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@ export function filesize(size: number) {
|
|||
return (size / Math.pow(num, 4)).toFixed(2) + 'T' //T
|
||||
}
|
||||
|
||||
/*
|
||||
随机id
|
||||
*/
|
||||
export const randomId = function () {
|
||||
return Math.floor(Math.random() * 10000) + ''
|
||||
}
|
||||
|
||||
/*
|
||||
获取文件后缀
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue