说明
带聊天框、带复制,其他自定义
html
<template>
<view class="container box-border">
<view @click="closeOutside">
<wd-drop-menu>
<wd-drop-menu-item v-model="selectLLM" :options="optionsLLM" @change="LLMChage" />
<wd-drop-menu-item title="次数" ref="dropMenu">
<view class="p-3">剩余可使用次数:9999</view>
</wd-drop-menu-item>
</wd-drop-menu>
</view>
<scroll-view
scroll-y
class="w-[96%] m-auto rounded-2 box-border mt-4 p-3 bg-white min-h-[20vh] max-h-[76vh]"
>
<view class="text-[#999] text-[13px] pb-3" v-if="chatData.length == 0">
请在下方输入框输入内容进行使用
</view>
<!-- 返回数据内容 -->
<view class="list">
<view v-for="(item, index) in chatData" :key="index">
<!-- 用户数据 -->
<view class="right text-right" v-if="item.type == 'user'">
<view class="flex items-center justify-end">
<view class="text-[14px] font-bold pr-1">用户1</view>
<wd-img
:width="36"
:height="36"
round
src="/images/20231218/202312181827416f1553423.png"
/>
</view>
<view>{{ item.content }}</view>
</view>
<!-- Ai 回复 -->
<view class="left mb-[40rpx]" v-else>
<view class="flex items-center">
<wd-img
:width="36"
:height="36"
round
src="/uploads/images/20231221/2023122119253887c6f3341.png"
/>
<view class="text-[14px] font-bold pl-1">美育云AI</view>
</view>
<view
class="bg-[#f5f5f5] p-2 mt-1 rounded-1"
v-html="item.content.length > 0 ? item.content : responseData"
></view>
<view
class="flex items-center mt-1"
v-if="item.content.length > 0"
@click="copyText(item.content)"
>
<wd-icon name="edit-1" size="22px" color="#999"></wd-icon>
<view class="pl-1 text-#999">复制</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 输入框,发送按钮 -->
<view class="w-full mt-4 p-3 box-border fixed bottom-5">
<wd-input
v-model="inputText"
placeholder="请输入内容"
class="w-full m-auto rounded-[10px]"
no-border
custom-input-class="inputTextClass"
>
<template #suffix>
<view v-if="!isLoading">
<wd-button type="primary" @click="handleClick">发送</wd-button>
</view>
<view class="text-[13px] py-2 text-#999 pr-2" v-else>加载中...</view>
</template>
</wd-input>
</view>
</view>
</template>
<script lang="ts" setup>
import { marked } from 'marked'
import { ref } from 'vue'
import { useQueue } from 'wot-design-uni'
const { closeOutside } = useQueue()
const selectLLM = ref<number>(0)
const optionsLLM = ref<Record<string, any>>([
{ label: '智谱清言', value: 0 },
{ label: '通义千问', value: 1 },
])
// 定义完整聊天数据
const chatData = ref<Record<string, any>[]>([])
// 定义提问次数
const askTimes = ref<number>(0)
// 定义模型切换
function LLMChage({ value }) {
console.log(value)
}
// 点击复制
function copyText(text: string) {
// 去除html标签
text = text.replace(/<[^>]+>/g, '')
uni.setClipboardData({
data: text,
success: function () {
uni.showToast({
title: '复制成功',
icon: 'none',
})
},
})
}
// 输入文本
const inputText = ref<String>('')
// 定义响应数据
const responseData = ref<String>('')
// 定义点击事件
const handleClick = () => {
// 插入一条聊天数据
chatData.value.push({
type: 'user',
content: inputText.value,
})
sendRequest()
}
// 是否加载中
const isLoading = ref<Boolean>(false)
// 定义请求
const sendRequest = () => {
// 插入一条数据
chatData.value.push({
type: 'ai',
content: '',
})
const xhr = new XMLHttpRequest()
isLoading.value = true
// 打开 POST 请求
xhr.open('POST', 'https://open.bigmodel.cn/api/paas/v4/chat/completions', true)
// 设置自定义请求头
xhr.setRequestHeader('Authorization', 'Bearer 你自己的key')
xhr.setRequestHeader('Content-Type', 'application/json')
// 发送 POST 请求,带参数
xhr.send(
JSON.stringify({
model: 'glm-4-plus',
messages: [{ role: 'user', content: inputText.value }],
stream: true,
}),
)
// 监听流数据
xhr.onreadystatechange = async () => {
/**
* @author: xl
* @date: 2025/01/12
* @description: 智谱清言语言模型接入 wechat:suiyi01001
* @version: 1.0.0
*/
// 说明有数据返回
if (xhr.readyState === 3) {
// 分割数据
const jsonStringsArray = xhr.responseText
.split('\n')
.filter((line) => line.startsWith('data: '))
// 循环分割的数据
let dataText = ''
for (var i = 0; i < jsonStringsArray.length; i++) {
if (jsonStringsArray[i].slice(6) == '[DONE]') {
console.log('SSE 已关闭连接')
setTimeout(() => {
isLoading.value = false
// 修改聊天数据
console.log('responseData: ', chatData.value, chatData.value[chatData.value.length - 1])
// chatData.value.push({
// type: 'ai',
// content: responseData.value,
// })
chatData.value[chatData.value.length - 1].content = responseData.value
}, 1000)
} else {
const jsonObject = JSON.parse(jsonStringsArray[i].slice(6)) // 剔除"data:"前缀
// console.log('jsonObject: ', jsonObject);
const choices = jsonObject.choices // 提取choices数组
// 存在数据输出结果
if (jsonObject.choices && jsonObject.choices[0].delta) {
dataText += jsonObject.choices[0].delta.content
}
}
}
// 输出最终数据
responseData.value = await marked(dataText)
}
}
// 监听错误
xhr.onerror = (err) => {
console.error('SSE 错误:', err)
}
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
</style>