一. 申请deepseek访问key值

进入deepseek官网:https://www.deepseek.com。进入官网右上角【API开放平台】。
在这里插入图片描述
左侧菜单栏【API keys】,创建API key。
在这里插入图片描述

二.uniapp调用访问

核心:配置sse,接收流式数据,实现打字机效果。话不多说,上代码。

页面vue代码:

<template>
	<view class="setting">
		<view class="kefu-box">
	  <!-- 聊天内容 -->
			<scroll-view :scroll-y="true" id="scroll-view-content" class="scroll-h"
				:style="{height: scrollStyle.contentViewHeight + 'px'}"
			    :show-scrollbar="true"
				:scroll-with-animation="true" 
				:scroll-anchoring="true" 
				refresher-background="#f5f5f5" 
				:refresher-triggered="refreshTriggered"  
				:scroll-into-view="'chat-item-'+scrollMsgIdx"
				:scroll-top="scrollTop"
			>
					<view v-for="(item, index) in chatList" :key="index" class="content"
						:style="{ flexDirection: item.role == 'user' ? 'row-reverse' : '' }" :id="'chat-item-'+index">
						<!-- 头像 -->
						<image  v-if="item.role == 'assistant'" src='wxservice.png' mode="aspectFill" class="avatar-image"></image>
						<image  v-if="item.role == 'user'" src='wxme.png' mode="aspectFill" class="avatar-image"></image>
	        <!-- 用户名称、消息时间 -->
						<view class="user-chat-info">
							<view class="user-info" :class="item.role == 'user' ? 'order-2' : ''" :style="{ flexDirection: item.role == 'user' ? 'row-reverse' : '' }">
								<view class="user-chat-name" v-if="item.role == 'user'">
									用户
								</view>
								<view class="user-chat-name" v-if="item.role == 'assistant'">
									喝碗茶冷静一下
								</view>
							</view>
	          <!-- 聊天消息 -->
							<view class="content-text" :style="{ textAlign: item.role == 'user' ? 'right' : 'left' }">
								<view class="text-span" v-if="item.role == 'user'">
									<u-parse :content="item.content"></u-parse>
								</view>
								<view class="role-span" v-if="item.role == 'assistant'">
									<u-parse :content="item.content"></u-parse>
								</view>
							</view>
							<u-loading-icon color="#fcb08c" :show="item.role == 'assistant' && item.ds_loading" text="思考中......"></u-loading-icon>
						</view>
					</view>
			</scroll-view>
		</view>
		<!-- 底部发送信息 -->
		<view class="footerCon" :style="'transform: translate3d(0,' + percent + '%,0);'" ref="footerCon">
			<view class="footer row-bottom" ref="footer">
				<u--textarea style="width:80%;" type="text" placeholder="请输入内容" class="input" ref="input" @focus="focus" cursor-spacing="20" v-model="message" confirm-type="send" @confirm="sendTest('')" ></u--textarea>
				<view class="send" @click="sendTest('')">
					发送
				</view>
			</view>
		</view>
	</view>
	
</template>

<script>
import {StreamRequest,decodeUTF8} from '@/common/common.js'

export default {
	data() {
		return {
			message: '', //输入信息
			userInfo: '',  //用户信息
			chatList: [{ role: "assistant", content: "您好,我是喝碗茶冷静一下.您有什么问题可以问我,我会尽力解答(*^_^*)"}],  //信息列表
			knowledgeInfo: '', //知识库
			agentUser: '', //缓存信息
			constData: this.$constData,
			page: 1,
			pageSize: 10,
			totalPage: -1,//总聊天记录页数
			freshing: false,
			// 超出限制数组
			model: 'product',
			pid: 1,
			percent: 0,
			refreshTriggered: true,
			isSocketOpen: false,//webscoket是否已经打开
			scrollMsgIdx: 0, // 滚动条定位为到哪条消息
			isMorePage: false,//是否多屏数据
			re_message:'',
			scrollTop:0,
			scrollStyle:{
				pageHeight: 0,
				contentViewHeight: 0,
			    footViewHeight: 90,
				mitemHeight: 0
			},  //滚动插件style
		};
	},
	created () {
	  let res = uni.getSystemInfoSync();   //获取手机可使用窗口高度     api为获取系统信息同步接口
	  this.scrollStyle.pageHeight = res.windowHeight;
	  this.scrollStyle.contentViewHeight = res.windowHeight - uni.getSystemInfoSync().screenWidth / 750 * (100) - 70; //像素   因为给出的是像素高度 然后我们用的是upx  所以换算一下 
	  //this.scrollToBottom();   //创建后调用回到底部方法
		this.chatList = [{ role: "assistant", content: "您好,我是喝碗茶冷静一下.您有什么问题可以问我,我会尽力解答(*^_^*)"}]  //信息列表
	},
	
	watch: {
		chatList(newVal, oldVal) {
			this.chatList = newVal;
		}
	},

	methods: {
		//获取聊天消息的高度
		getChatHeight(){
			if(!this.isMorePage){
				let query = uni.createSelectorQuery().in(this);
				query.select('#scroll-view-content').boundingClientRect(data => {
					if (data) {
						let systemInfo = uni.getStorageSync('systemInfo');
						let editHeight = 65;//有表情框
						let toMoreHeight = 0;
						let caHeight = Number(systemInfo.windowHeight) - Number(editHeight)-Number(data.height)- toMoreHeight;
						this.isMorePage = Number(caHeight) <= 0;//是否只有一屏//内容的高度大于可用窗口的高度-输入文字的高度
						this.scrollTop = data.height * 100;
					}
				}).exec();
			}
		},


		scrollToBottom(){
			let size = this.chatList.length;
			if (size > 0) {
				this.scrollToMsgIdx(size - 1);
			}
		},

		scrollToMsgIdx(idx) {
			// 如果scrollMsgIdx值没变化,滚动条不会移动
			if (idx == this.scrollMsgIdx && idx > 0) {
				let that = this;
				let query = uni.createSelectorQuery();
				query.selectAll('.content').boundingClientRect();
				query.select('#scroll-view-content').boundingClientRect();
				query.exec((res) => {
				    that.scrollStyle.mitemHeight = 0;
				    res[0].forEach((rect) => that.scrollStyle.mitemHeight = that.scrollStyle.mitemHeight + rect.height + 40)   //获取所有内部子元素的高度
				    if (that.scrollStyle.mitemHeight > (that.scrollStyle.contentViewHeight - 100)) {   //判断子元素高度是否大于显示高度
				        that.scrollTop = that.scrollStyle.mitemHeight - that.scrollStyle.contentViewHeight    //用子元素的高度减去显示的高度就获益获得序言滚动的高度
				    }
				})
			}
			this.$nextTick(() => {
				this.scrollMsgIdx = idx;
			});

		},

		focus() {
			this.getChatHeight();
		},

		//发送消息
		async sendTest(message,flag) {
			if (!this.message && !message) {
				return uni.showToast({
					title: '消息为空',
					icon: 'none'
				})
			}
			var content = {
				model: "deepseek-reasoner", // 指定 R1 模型:cite[4]:cite[8]
				messages: [{ role: "user", content: this.message }],
				stream: true,
				max_tokens: 4096,
				// temperature:0.2,
			}
			this.chatList.push({ role: "user", content: this.message })
			this.chatList.push({ role: "assistant", content: "信息接收中......"})
			//this.getChatHeight();
			this.scrollToBottom();
			this.message = "";
			
			try {
				const requestTask= await StreamRequest(content)
				// 返回请求头信息
				requestTask.onHeadersReceived((e)=>{
					this.chatList[this.chatList.length - 1].content = ""
					this.chatList[this.chatList.length - 1].ds_loading = true
				})
				
				// 成功回调 返回流传输信息 返回arrayBuffer
				requestTask.onChunkReceived((res)=>{
					var decodedData = this.decode(res.data);
					if (decodedData) {
						this.chatList[this.chatList.length - 1].content += decodedData
						// 数据和DOM都更新完成后,获取容器高度
						//this.getChatHeight();
						this.scrollToBottom();
					}
				})
			} catch (err) {
				this.chatList.push({ role: "assistant", content: "服务暂不可用,请重试。" });
			}
		},
		
		//数据解析器(处理SSE格式)
		decode(data) {
		    const text = decodeUTF8(data);
		    const lines = text.split('\n');
		    let result = '';
		    
		    for (let line of lines) {
		        if (line.startsWith('data: ')) {
		            const jsonData = line.slice(6).trim();
		            
		            // 结束标识处理
		            if (jsonData === '[DONE]') {
						this.chatList[this.chatList.length - 1].ds_loading = false;
						return result;
					}
		            
		            // 清理控制字符(防止JSON解析失败)
		            const cleanedData = jsonData.replace(/[\u0000-\u001F\u007F-\u009F]/g, '');
		            
		            try {
		                const parsedData = JSON.parse(cleanedData);
		                // 提取AI生成内容
		                result += parsedData.choices[0].delta.reasoning_content || '';
		            } catch (e) {
		                console.error('解析失败:', e);
		            }
		        }
		    }
		    return result;
		},
	},
}
</script>

<style lang="scss">

.scroll-h {
	max-height: calc(100vh - 100px);
	padding-bottom: 20upx;
	overflow-anchor: auto;
	transform: rotate(360deg);
}
.autoHeight{
	height: calc(100% - 65px);
	overflow-y: auto;
}

.scroll-h-icon.autoHeight {
	height: calc(100% - 380px);
	overflow-y: auto;
}


.user-chat-info {
	flex: 1;
}

.user-info{
	margin-bottom: 10upx;
}
.order-2.user-info {
	text-align: right;
}

.setting {
	min-height: auto;
	background: #f5f5f5;
}

.kefu-box {
	padding: 0 20upx;
	height: calc(100vh - 100px);

	.content {
		transform: rotate(360deg);
		display: flex;
		// align-items: center;
		padding-bottom: 35upx;
	}

	.avatar-image {
		width: 60upx;
		height: 60upx;
		border-radius: 50%;
		margin: 10upx 10upx;
	}


	.content-text-knowledge {
		padding: 10upx 15upx;
		background-color: #fff !important;
		border-radius: 5px;
		margin-top: 10upx;
		position: relative;

		.knowledge-title {
			font-size: 20upx;
			// font-weight: 550;
			margin-bottom: 20upx;
		}

		.border-none {
			:last-child {
				border: none !important;
			}

			.iconfont {
				font-size: 24upx !important;
			}
		}

		.knowledge-flxe {
			min-width: 60upx;
			max-width: 600upx;
			padding: 10px 0;
			display: flex;
			justify-content: space-between;
			align-items: center;
			cursor: pointer;
			border-bottom: 1px solid #f5f5f5;
			font-size: 20upx;
		}
	}

	.content-text {
		// background-color: #c7dcfa;
		margin-top: 10upx;
		position: relative;

		.text-span {
			max-width: 600upx;
			border-radius: 5px;
			padding: 10upx 15upx;
			display: inline-block;
			background-color: #c7dcfa;
			text-align: left;
		}

		.avatar-image {
			max-width: 300upx;
			max-height: 300upx;
		}
		
		.role-span {
			max-width: 600upx;
			border-radius: 5px;
			padding: 10upx 15upx;
			display: inline-block;
			background-color: #fcb08c;
			text-align: left;
		}
	}

	.online-time {
		color: #666;
		font-size: 20upx;
	}
}



.user-chat-name {
	color: #000;
}

.setting-item {
	padding: 25upx 20upx;
	border-bottom: 2upx #dfdfdf solid;
	font-size: 20upx;
}

.avatar {
	width: 60upx;
	height: 60upx;
	border-radius: 50%;
	overflow: hidden;
}

.item-value {
	color: #999999;
}

.send {
	width: 100upx;
	text-align: center;
	height: 60upx;
	line-height: 60upx;
	background-color: #f56c6c;
	font-size: 12px;
	color: #ffffff;
	margin-left: 10px;
}

.input {
	max-height: 150upx;
	overflow-y: auto;
	overflow-x: hidden;
	flex: 1;
	margin: 0 10upx;
	border-radius: 10upx;
	background-color: #e5e5e5;
	/* padding: 17upx 30upx; */
	height: 60upx;
	padding: 0 10upx;
}

.footer {
	width: 90%;
	background-color: #fff;
	padding: 17upx 26upx;
	display: flex;
	align-items: center;
}

.footer image {
	width: 61upx;
	height: 60upx;
	display: block;
}

.footerCon {
	position: fixed;
	bottom: 0upx;
	min-height: 100upx;
	width: 100%;
	transition: all 0.005s cubic-bezier(0.25, 0.5, 0.5, 0.9);
	background-color: #fff;
}

.footerCon .on {
	bottom: 300upx;
	transform: translate3d(0, 0, 0) !important;
}


.bottom-icon {
	padding: 15upx;
}
.chat-image{
	max-width: 375upx !important;
}
.load-more{
	text-align: center;
    color: #1423fc;
    padding: 5px 0;
	transform: rotate(180deg);
}
textarea{
	background-color: #ebeced; 
	border-radius: 5px; 
	padding:5px;
}
.u-loading-icon{
	// width: 25px!important;
}
</style>

common.js代码

export function StreamRequest(data)  {
    return new Promise((resolve, reject) => {
        const response = uni.request({
            url: 'https://api.deepseek.com/chat/completions', // 请求地址
            method: "POST", // 你的请求方法
            data: data,
            header: {
                'Content-Type': 'application/json', // 声明接受事件流
                'Authorization': "Bearer 你申请的key"
            },
            responseType: "text",
            enableChunked: true, // 开启流传输
            success: (res) => {
                resolve(res)
            },
            fail: (err) => {
                reject(err)
            },
        })

        // 返回请求的响应
        resolve(response)
    })
}



export function decodeUTF8(data) {
    // 将二进制数据转为Uint8数组
    const uint8Array = new Uint8Array(data);
    
    // 传统方式转换字符串(兼容旧环境)
    let string = '';
    for (let i = 0; i < uint8Array.length; i++) {
        string += String.fromCharCode(uint8Array[i]);
    }
    
    // 双重解码处理特殊字符(如中文)
    return decodeURIComponent(escape(string));
}

三.附效果视频

请添加图片描述

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐