Merge branch 'main' of github.com:maxkb-dev/maxkb

This commit is contained in:
shaohuzhang1 2023-11-28 18:26:53 +08:00
commit e1e050d153
7 changed files with 4678 additions and 46 deletions

4522
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) + ''
}
/*
*/