feat: 增加知识库
This commit is contained in:
parent
8a636bfde3
commit
065d1b556f
@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/feige996/unibest">
|
||||
<img width="160" src="./src/static/logo.svg">
|
||||
<img width="160" src="./src/static/logo.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
"versionCode": "100",
|
||||
"transformPx": false,
|
||||
"app-plus": {
|
||||
|
||||
"usingComponents": true,
|
||||
"nvueStyleCompiler": "uni-app",
|
||||
"compilerVersion": 3,
|
||||
|
@ -8,10 +8,10 @@
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col h-screen bg-gray-50">
|
||||
<div class="flex flex-col h-screen bg-#ffffff">
|
||||
<!-- Navigation Bar -->
|
||||
<div
|
||||
class="flex-none flex items-center justify-between px-5 py-3 bg-white shadow-md h-10 pt-10 z-999"
|
||||
class="flex-none flex items-center justify-between px-5 py-3 bg-white shadow-md h-20 pt-10 z-999 fixed top-0 w-full box-border"
|
||||
>
|
||||
<image src="/static/aichat/black.png" class="w-3 h-4" @click="goBack" />
|
||||
<div class="text-lg font-medium ml-12">小墨</div>
|
||||
@ -25,14 +25,14 @@
|
||||
<!-- 消息区 -->
|
||||
<div
|
||||
:class="[
|
||||
'flex relative p-b-10 box-border',
|
||||
showActions ? (uploadList.length ? 'h-118' : 'h-137') : 'h-167',
|
||||
'flex fixed top-0 w-full p-b-10 box-border mt-20',
|
||||
showActions ? (uploadList.length ? 'h-118' : 'h-151') : 'h-171',
|
||||
]"
|
||||
>
|
||||
<!-- 背景层 -->
|
||||
<div
|
||||
v-if="!messages.length"
|
||||
class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none"
|
||||
class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none bg-#ffffff"
|
||||
>
|
||||
<image src="/static/aichat/logo.png" class="w-20 h-24 mb-4" @click="newChat" />
|
||||
<view class="text-xl font-medium mb-1">嗨! 我是小墨</view>
|
||||
@ -41,7 +41,7 @@
|
||||
|
||||
<div
|
||||
ref="scrollEl"
|
||||
class="flex-1 overflow-y-auto bg-gray-50"
|
||||
class="flex-1 overflow-y-auto bg-#ffffff"
|
||||
:class="showActions ? 'pb-44' : 'pb-16'"
|
||||
>
|
||||
<div :class="['relative z-10 px-4 py-6', showActions ? 'mb--11 h-105' : 'mb--21 h-135']">
|
||||
@ -60,11 +60,11 @@
|
||||
/>
|
||||
<view
|
||||
class="relative max-w-[76.5%] mt-5"
|
||||
:class="idx === messages.length - 1 ? 'mb-10' : 'mb-3'"
|
||||
:class="idx === messages.length - 1 ? 'mb-20' : 'mb-3'"
|
||||
>
|
||||
<view
|
||||
:class="[
|
||||
'absolute -top-5 text-xs text-gray-400 w-20 text-right',
|
||||
'absolute -top-5 text-xs text-gray-400 w-20 text-right ',
|
||||
msg.role === 'assistant' ? 'left--4' : 'right-0',
|
||||
]"
|
||||
>
|
||||
@ -72,12 +72,22 @@
|
||||
</view>
|
||||
<view
|
||||
:class="[
|
||||
'py-3 pl-3 rounded-md break-words mb-3 tracking-[2rpx] ',
|
||||
'py-3 pl-3 rounded-md break-words mb-3 tracking-[2rpx] min-w-10 min-h-2 ',
|
||||
msg.role === 'assistant'
|
||||
? 'bg-[#f9f8fd] text-black shadow '
|
||||
: 'bg-[#45299e] text-white ',
|
||||
]"
|
||||
>
|
||||
<wd-loading
|
||||
v-show="msg.role === 'assistant' && msgLoading && idx === messages.length - 1"
|
||||
:size="20"
|
||||
color="#e3e3e3"
|
||||
custom-class="loading-black"
|
||||
:class="[
|
||||
'absolute top-1.5 text-xs text-gray-400 w-20 text-right',
|
||||
msg.role === 'assistant' ? 'left-1' : 'right-0',
|
||||
]"
|
||||
/>
|
||||
<!-- 图片消息 -->
|
||||
<scroll-view
|
||||
scroll-x
|
||||
@ -88,21 +98,31 @@
|
||||
<view
|
||||
v-for="(file, fileIdx) in msg.content"
|
||||
:key="fileIdx"
|
||||
style="flex: 0 0 2.5rem"
|
||||
class="relative h-10 rounded-md overflow-hidden mr-1"
|
||||
class="relative rounded-md overflow-hidden mr-1"
|
||||
:class="{
|
||||
'w-60 h-60': msg.content.length == 1,
|
||||
'w-15 h-15': msg.content.length == 2,
|
||||
'w-30 h-15 ': msg.content.length == 3,
|
||||
' h-15 flex-grow-0 flex-shrink-0 basis-10': msg.content.length >= 4,
|
||||
}"
|
||||
>
|
||||
<view
|
||||
v-if="showImageMask && fileIdx === 4"
|
||||
class="absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center"
|
||||
:class="{ 'bg-black bg-opacity-70': showImageMask && fileIdx === 3 }"
|
||||
v-if="showImageMask && fileIdx === 3"
|
||||
:class="{ 'bg-black bg-opacity-70': showImageMask && fileIdx === 4 }"
|
||||
>
|
||||
+{{ msg.content.length - 3 }}
|
||||
+{{ msg.content.length - 4 }}
|
||||
</view>
|
||||
|
||||
<img
|
||||
<image
|
||||
v-if="file.uploadFileType === uploadFileTypeEm.image"
|
||||
:src="file.url || file.tempFilePath"
|
||||
class="w-full h-full object-cover"
|
||||
:class="{
|
||||
'w-100% h-100%': msg.content.length == 1,
|
||||
'w-15 h-15': msg.content.length == 2,
|
||||
'w-40 h-15 ': msg.content.length == 3,
|
||||
'w-40 h-15 object-cover': msg.content.length >= 4,
|
||||
}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
@ -155,11 +175,7 @@
|
||||
class="absolute bottom--3.5 flex space-x-3 ml-1"
|
||||
>
|
||||
<image src="/static/aichat/copy.png" class="w-4 h-4" @click="copyText(msg)" />
|
||||
<image
|
||||
src="/static/aichat/resect.png"
|
||||
class="w-4.3 h-4"
|
||||
@click="refreshText(msg)"
|
||||
/>
|
||||
<image src="/static/aichat/resect.png" class="w-4.3 h-4" @click="refreshText()" />
|
||||
</view>
|
||||
</view>
|
||||
<image
|
||||
@ -177,7 +193,7 @@
|
||||
<div
|
||||
:class="[
|
||||
'fixed bottom-0 left-0 right-0 bg-white z-[80] overflow-hidden transition-all duration-300',
|
||||
showActions ? (uploadList.length ? 'h-58' : 'h-40') : 'h-20',
|
||||
showActions ? (uploadList.length ? 'h-60' : 'h-40') : 'h-20',
|
||||
]"
|
||||
>
|
||||
<!-- 上传列表 -->
|
||||
@ -234,16 +250,42 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 知识库-->
|
||||
|
||||
<div
|
||||
@click="toggleKnowledge"
|
||||
v-if="!uploadList.length"
|
||||
class="fixed left-[32rpx] right-0 z-[90] h-8 w-23 flex items-center justify-between px-3 box-border rounded-1 transition-all duration-300"
|
||||
:class="[
|
||||
knowledgeOpen ? 'bg-#eee9f8' : 'bg-[#F9F9F9]',
|
||||
showActions ? (uploadList.length ? 'bottom-31' : 'bottom-41') : 'bottom-21',
|
||||
]"
|
||||
>
|
||||
<image
|
||||
:src="
|
||||
knowledgeOpen
|
||||
? '/static/aichat/Knowledge-open.png'
|
||||
: '/static/aichat/Knowledge-close.png'
|
||||
"
|
||||
class="w-4 h-3.5 mt-1"
|
||||
/>
|
||||
<div
|
||||
:class="['text-[26rpx] transition-colors', knowledgeOpen ? 'text-#46299D' : 'text-black']"
|
||||
>
|
||||
知识库
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入 + 切换 -->
|
||||
<view class="flex items-center px-4 py-2.5 border-t border-t-solid border-[#E7E7E7]">
|
||||
<input
|
||||
v-model="inputText"
|
||||
@keyup.enter="sendText"
|
||||
@keyup.enter="sendText('')"
|
||||
placeholder="想对我说点什么~"
|
||||
class="flex-1 h-10 px-3 border border-gray-100 bg-[#f9f9f9] rounded-full focus:outline-none"
|
||||
class="flex-1 h-10 px-3 border border-gray-100 bg-[#f9f9f9] rounded-1 focus:outline-none"
|
||||
/>
|
||||
<image
|
||||
v-show="!knowledgeOpen"
|
||||
src="/static/aichat/add-circle.png"
|
||||
class="w-7 h-7 mx-3"
|
||||
@click="toggleActions"
|
||||
@ -255,8 +297,9 @@
|
||||
<image
|
||||
src="/static/aichat/enter.png"
|
||||
class="w-7 h-7"
|
||||
@click="sendText"
|
||||
@click="sendText('')"
|
||||
:disabled="loading"
|
||||
:class="[knowledgeOpen ? 'ml-2' : 'ml-0']"
|
||||
/>
|
||||
</view>
|
||||
|
||||
@ -264,7 +307,10 @@
|
||||
<transition name="slide-up">
|
||||
<view
|
||||
v-show="showActions"
|
||||
class="flex justify-around items-center h-20 bg-white border-t border-t-solid border-[#E7E7E7]"
|
||||
:class="[
|
||||
'flex justify-around items-center h-20 bg-white border-t border-t-solid border-[#E7E7E7]',
|
||||
showActions ? (uploadList.length ? 'pt-1' : 'pt-1') : 'pt-0',
|
||||
]"
|
||||
>
|
||||
<view class="flex flex-col items-center">
|
||||
<image src="/static/aichat/phone-img.png" class="w-13 h-13" @click="onPickImage" />
|
||||
@ -433,13 +479,12 @@ async function goChat(listUuid: string) {
|
||||
const listUuid = ref('')
|
||||
|
||||
async function createChatSession() {
|
||||
console.log(token.value, 'wwww')
|
||||
try {
|
||||
const createResp: any = await uni.request({
|
||||
url: `${baseUrl}/chat/create`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
gptModel: 'gpt-3.5-turbo',
|
||||
gptModel: 'gpt-4-vision-preview',
|
||||
},
|
||||
header: {
|
||||
Authorization: token.value,
|
||||
@ -487,7 +532,7 @@ async function fetchHistoryDiets(value) {
|
||||
method: 'POST',
|
||||
data: {
|
||||
listUuid: value,
|
||||
gptModel: 'gpt-3.5-turbo',
|
||||
gptModel: 'gpt-4-vision-preview',
|
||||
},
|
||||
header: {
|
||||
Authorization: token.value,
|
||||
@ -501,7 +546,9 @@ async function fetchHistoryDiets(value) {
|
||||
console.error('fetchHistoryList error:', err)
|
||||
}
|
||||
}
|
||||
const token = ref<string>('')
|
||||
const token = ref<string>(
|
||||
'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941ca1430937103230a1e32a1715f569f3efdbe6f8cb8b7b8642bd679668081b9b08f693d1b5be6002d936ec51e1e3e0c4927de9e32ac99a109b326e5d2bda27ec87624bb416ec70d2a95a2e190feeba9f0d6bae8571b3dfe89c824712344759a8f2bff9d70747c52525cf6a5614f9c770bca461a9b9c247b6dca97bcf83bbaf99bb726752c4fe1e9a4aa7de5c4cf3e88a3e480801280d45cdc124f9d8221105d852945dc6ce10bc1647e4f09dff4d52ffdfc3ee452912e5908e2c574b766448c260449904f2a3c5ab9ed0235cfc8d5ba01f5d7c370733b136f7775849b03324fc6532d108779f9a7ff8dc40f235c56fec1b7a11ad61ac7e0a6a86f198079f4b92d61',
|
||||
)
|
||||
const userInfo = ref<any>({})
|
||||
const refreshToken = ref<string>('')
|
||||
const statusBarHeight = ref<number>(0)
|
||||
@ -513,7 +560,7 @@ onMounted(() => {
|
||||
|
||||
const init = async () => {
|
||||
const wv = plus.webview.currentWebview()
|
||||
token.value = wv.token || uni.getStorageSync('token') || import.meta.env.VITE_DEV_TOKEN
|
||||
// token.value = wv.token || uni.getStorageSync('token') || import.meta.env.VITE_DEV_TOKEN
|
||||
userInfo.value = JSON.parse(wv.userInfo) || {}
|
||||
refreshToken.value = wv.refreshToken || uni.getStorageSync('refreshToken')
|
||||
statusBarHeight.value = wv.statusBarHeight || uni.getSystemInfoSync().statusBarHeight
|
||||
@ -558,6 +605,9 @@ const onScroll = (e) => {
|
||||
|
||||
//查看更多图片
|
||||
const previewMoreImg = (files) => {
|
||||
uni.removeStorageSync('previewImages')
|
||||
uni.removeStorageSync('previewVideos')
|
||||
|
||||
uni.setStorageSync('previewImages', files)
|
||||
|
||||
uni.navigateTo({
|
||||
@ -567,6 +617,9 @@ const previewMoreImg = (files) => {
|
||||
|
||||
//查看视频
|
||||
const previewVideo = (files) => {
|
||||
uni.removeStorageSync('previewVideos')
|
||||
uni.removeStorageSync('previewImages')
|
||||
|
||||
uni.setStorageSync('previewVideos', files)
|
||||
|
||||
uni.navigateTo({
|
||||
@ -769,7 +822,6 @@ const uploadFile = (file: UploadFile) => {
|
||||
// 照片
|
||||
const onPickImage = () => {
|
||||
loading.value = true
|
||||
|
||||
uni.chooseImage({
|
||||
count: 10, // 最多选择9张
|
||||
sizeType: ['original', 'compressed'],
|
||||
@ -818,7 +870,7 @@ const onPickVideo = () => {
|
||||
const onPickFile = () => {
|
||||
uni.chooseFile({
|
||||
count: 10,
|
||||
extension: uploadConfig.file.supportType,
|
||||
type: uploadConfig.file.supportType,
|
||||
success: (res: any) => {
|
||||
console.log(res)
|
||||
// 开始上传
|
||||
@ -859,11 +911,17 @@ const previewFile = (url: string) => {
|
||||
url: '/pages/webview/index?link=' + encodeURIComponent(url),
|
||||
})
|
||||
}
|
||||
|
||||
const msgLoading = ref(true)
|
||||
// 发送消息
|
||||
async function sendText(msg) {
|
||||
async function sendText(msgData) {
|
||||
msgLoading.value = true
|
||||
const text = inputText.value.trim()
|
||||
if (!text || loading.value) return
|
||||
if (!text) {
|
||||
uni.showToast({ title: '请输入信息', icon: 'error' })
|
||||
|
||||
return
|
||||
}
|
||||
if (loading.value) return
|
||||
|
||||
//获取本次发送的消息和文件
|
||||
const tempUploadList = Object.assign([], uploadList)
|
||||
@ -930,9 +988,10 @@ async function sendText(msg) {
|
||||
content: text,
|
||||
timestamp: new Date(),
|
||||
}
|
||||
|
||||
addMessage(aiMsg)
|
||||
//清除上传列表
|
||||
uploadList.splice(0, uploadList.length)
|
||||
// resds.value = historyUserMsgs
|
||||
|
||||
const body: IGptRequestBody = {
|
||||
model: 'gpt-4-vision-preview',
|
||||
@ -942,7 +1001,7 @@ async function sendText(msg) {
|
||||
top_p: 1,
|
||||
presence_penalty: 0,
|
||||
frequency_penalty: 0,
|
||||
messages: historyUserMsgs,
|
||||
messages: msgData || historyUserMsgs,
|
||||
stream: true,
|
||||
}
|
||||
|
||||
@ -979,7 +1038,10 @@ async function sendText(msg) {
|
||||
const delta = json.choices?.[0]?.delta?.content
|
||||
|
||||
if (delta) {
|
||||
msgLoading.value = false
|
||||
aiMsg.content += delta
|
||||
//每次更新messages消息,实现流式输出
|
||||
messages[messages.length - 1] = { ...aiMsg }
|
||||
scrollToBottom()
|
||||
console.log('2')
|
||||
}
|
||||
@ -988,8 +1050,6 @@ async function sendText(msg) {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加AI消息
|
||||
addMessage(aiMsg)
|
||||
//更新上下文消息
|
||||
historyUserMsgs.push(aiMsg)
|
||||
scrollToBottom()
|
||||
@ -1016,10 +1076,19 @@ function copyText(msg: IMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
function refreshText(msg: IMessage) {
|
||||
const lastUserMsg = historyUserMsgs[historyUserMsgs.length - 1]
|
||||
function refreshText() {
|
||||
const lastUserMsg = historyUserMsgs[historyUserMsgs.length - 2]
|
||||
// resds.value = lastUserMsg
|
||||
|
||||
sendText(lastUserMsg)
|
||||
}
|
||||
|
||||
const knowledgeOpen = ref(false)
|
||||
function toggleKnowledge() {
|
||||
knowledgeOpen.value = !knowledgeOpen.value
|
||||
showActions.value = false
|
||||
rotation.value = 0
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -1,22 +1,31 @@
|
||||
<template>
|
||||
<view class="image-gallery grid grid-cols-4 gap-1 p-4" v-if="imageList.length > 0">
|
||||
<view class="flex flex-col h-screen bg-#ffffff">
|
||||
<view
|
||||
v-for="(image, index) in imageList"
|
||||
:key="index"
|
||||
class="aspect-square overflow-hidden group"
|
||||
class="flex-none flex items-center justify-between px-5 py-3 bg-white shadow-md h-20 pt-10 z-999 fixed top-0 w-full box-border"
|
||||
>
|
||||
<image
|
||||
:src="image.url"
|
||||
mode="aspectFill"
|
||||
class="w-full h-full"
|
||||
@click="handleImageClick(index)"
|
||||
/>
|
||||
<image src="/static/aichat/black.png" class="w-3 h-4 mt-1" @click="goBack" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="image-gallery grid grid-cols-4 gap-1 p-4 mt-20" v-if="imageList.length > 0">
|
||||
<view
|
||||
v-for="(image, index) in imageList"
|
||||
:key="index"
|
||||
class="aspect-square overflow-hidden group"
|
||||
>
|
||||
<!-- <image
|
||||
:src="image.url"
|
||||
mode="aspectFill"
|
||||
class="w-full h-full"
|
||||
@click="handleImageClick(index)"
|
||||
/> -->
|
||||
|
||||
<view v-if="videoList.length > 0" class="flex items-center justify-center">
|
||||
<view v-for="(video, index) in videoList" :key="index" class="w-full h-50">
|
||||
<video :src="video" class="w-full h-full" controls :poster="video.url"></video>
|
||||
<wd-img :width="100" :height="100" :src="image.url" :enable-preview="true" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="videoList.length > 0" class="flex items-center justify-center mt-30">
|
||||
<view v-for="(video, index) in videoList" :key="index" class="w-full h-50">
|
||||
<video :src="video" class="w-full h-full" controls :poster="video.url"></video>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@ -30,8 +39,12 @@ const imageList = ref([])
|
||||
const videoList = ref([])
|
||||
|
||||
// 图片点击处理
|
||||
const handleImageClick = (index) => {
|
||||
uni.previewImage({ urls: [imageList.value[index].url] })
|
||||
// const handleImageClick = (index) => {
|
||||
// uni.previewImage({ urls: [imageList.value[index].url] })
|
||||
// }
|
||||
// 返回上一个页面
|
||||
const goBack = () => {
|
||||
uni.navigateBack({ delta: 1 })
|
||||
}
|
||||
|
||||
// 生命周期钩子
|
||||
|
BIN
src/static/aichat/Knowledge-close.png
Normal file
BIN
src/static/aichat/Knowledge-close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 776 B |
BIN
src/static/aichat/Knowledge-open.png
Normal file
BIN
src/static/aichat/Knowledge-open.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 827 B |
BIN
src/static/logo.png
Normal file
BIN
src/static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Loading…
Reference in New Issue
Block a user