vue 仿deepseek前端开发一个对话界面
后端:调用deepseek的api,所以返回数据格式和deepseek相同
{"model": "DeepSeek-R1-Distill-Qwen-1.5B", "choices": [{"index": 0, "delta": {"role": "assistant", "content": ",有什么", "tool_calls": null}, "finish_reason": null, "logprobs": null}], "usage": {"prompt_tokens": 5, "completion_tokens": 11, "total_tokens": 16}, "id": "chatcmpl-203a3024a36e4c02b02200ca47d8901e", "object": "chat.completion.chunk", "created": 1741766893}

前端开发的几个注意点:
- 将后端返回的文本转换为html展示在页面上,注意调整样式
- 模拟打字形式,页面随之滚动
- 撑开的输入框和对话内容部分样式调整
- 测试多种文本输入,例如含有html标签的
- 记录思考时间,思考内容模仿deepseek做收起
这只是初步的项目,仅支持文本输入
懒得分步写了,直接贴完整代码吧
有些地方可能写得比较繁琐
比如判断思考内容那段,只能通过think标签判断吗?
请各位多多指点,欢迎交流!!
<template><div class="talk-window"><div class="talk-title"><p>{{ answerTitle }}</p><el-input v-model="choicedTasks.name" placeholder="请选择任务" class="sangedianBtn" readonly><template #append><el-button class="ec-font icon-sangedian" @click="isShowTask = true" /></template></el-input></div><div class="talk-container"><div class="talk-welcome" v-if="contentList.length == 0"><h1>{{ welcome.title }}</h1><p>{{ welcome.desc }}</p></div><div class="talk-box" v-else :style="{ height: answerContHeight }"><div ref="logContainer" class="talk-content"><el-row v-for="(item, i) in contentList" :key="i" class="chat-assistant"><transition name="fade"><div :class="['answer-cont', item.type === 'send' ? 'end' : 'start']"><img v-if="item.type == 'answer'" :src="welcome.icon" /><div :class="item.type === 'send' ? 'send-item' : 'answer-item'"><div v-if="item.type == 'answer'" class="hashrate-markdown" v-html="item.message" /><div v-else>{{ item.message }}</div></div><!-- 增加复制 --><!-- <div v-if="item.type == 'answer' && !isTalking"><el-tooltip centent="复制"><i class="ec-font icon-ticket" @click="copyMsg(item.message)" /></el-tooltip></div> --></div></transition></el-row></div><div style="text-align: center; margin-top: 10px"><el-button class="chat-add" @click="newChat"><i class="ec-font icon-tianjia1" />新建对话</el-button></div></div><div class="talk-send"><textarea@keydown.enter="enterMessage"ref="input"v-model="inputMessage"@input="adjustInputHeight"placeholder="输入消息...":rows="2" /><!-- <el-inputv-model="inputMessage":autosize="{ minRows: 2, maxRows: 5 }"type="textarea"@keyup.enter="enterMessage"placeholder="Please input" /> --><div class="talk-btn-cont" style="text-align: right"><img @click="sendMessage" :src="iconImg" /></div></div></div><copyrightContent :systemNameOption="systemNameOption" /><taskDialog v-if="isShowTask" :choicedTasks="choicedTasks" v-model:isShow="isShowTask" @confirm="confirmTask" /></div>
</template><script>
import taskDialog from '@/views/chat/task/taskDialog.vue'
import copyrightContent from '@/views/chat/talking/components/copyright.vue'
import hljs from 'highlight.js'
import 'highlight.js/styles/a11y-dark.css'
import MarkdownIt from 'markdown-it'
import { VSConfig } from '/config/envConfig'window.hiddenThink = function (index) {// 隐藏思考内容if (document.getElementById(`think_content_${index}`).style.display == 'none') {document.getElementById(`think_content_${index}`).style.display = 'block'document.getElementById(`think_icon_${index}`).classList.replace('icon-a-xiangshang3', 'icon-a-xiangxia3')} else {document.getElementById(`think_content_${index}`).style.display = 'none'document.getElementById(`think_icon_${index}`).classList.replace('icon-a-xiangxia3', 'icon-a-xiangshang3')}
}export default {props: {isCollapsed: {type: Boolean,default: false},activeChat: String,activeTitle: String,chat_style_setting: {type: Object,default: function () {return {}}},systemNameOption: {type: Object,default: function () {return {}}}},components: { taskDialog, copyrightContent },data() {return {inputMessage: '',messages: [],choicedTasks: {uuid: '',name: ''},isShowTask: false,contentList: [],eventSourceChat: null,markdownIt: {},startAnwer: false,startTime: null,endTime: null,thinkTime: null,answerTitle: '',talkUUID: '',refreshHistoryFlag: false, // 是否已经生成了对话uuid,生成了的话就刷新历史列表msgHight: null,isTalking: false, //是否在对话中,处于对话中则展示休止按钮welcome: {title: '很高兴见到你!',desc: '我可以帮你写代码、读文件、写作各种创意内容,请把你的任务交给我吧~',icon: require('@/assets/image/AI.png')},store: {}}},watch: {activeTitle(val) {// 重命名了对话this.answerTitle = val},activeChat(val) {this.answerTitle = ''this.talkUUID = val || ''this.inputMessage = ''this.refreshHistoryFlag = falsethis.isTalking = falseif (val) {// 滚动回到顶部const logContainer = this.$refs.logContainerif (logContainer) {logContainer.scrollTop = 0}this.getTalkDetail()} else {this.contentList = []}this.eventSourceChat && this.eventSourceChat.close()},chat_style_setting(val) {this.welcome.title = val.welcome_speech_style || this.welcome.titlethis.welcome.desc = val.description_style || this.welcome.descthis.welcome.icon = val.icon_image || this.welcome.icon}},computed: {iconImg() {// 对话中可能有输入if (this.isTalking) {return require('/src/assets/image/chat/stop.png')} else {if (!this.inputMessage ||this.inputMessage.trim() === '' ||this.inputMessage.split(/\r?\n/).every((line) => line.trim() === '')) {return require('/src/assets/image/chat/unsend.png')} else {return require('/src/assets/image/chat/send.png')}}},answerContHeight() {// 回到初始值return this.msgHight == '56px' ? 'calc(100% - 140px)' : `calc(100% - 140px - ${this.msgHight} + 56px)`}},mounted() {this.store = mainStore()this.markdownIt = MarkdownIt({html: true,linkify: true,highlight: function (str, lang) {if (lang && hljs.getLanguage(lang)) {try {return hljs.highlight(str, { language: lang }).value} catch (__) {}}return '' // use external default escaping}})},methods: {getTalkDetail() {chat.historyDetail({uuid: this.activeChat}).then((res) => {this.contentList = []if (res.code == 0) {res.info.history_meta &&res.info.history_meta.forEach((item, index) => {if (item.conversation_type == 'Answer') {// 增加一个历史思考记录收起吧item.context = item.context.replace(/<think>\n\n<\/think>/g, '').replaceAll('<think>',`<div class="think-time">历史思考<i id="think_icon_${index}" onclick="hiddenThink(${index})" class="ec-font icon-a-xiangxia3"></i></div><section id="think_content_${index}">`).replaceAll('</think>', '</section>')item.context = this.markdownIt.render(item.context)}this.contentList.push({type: item.conversation_type == 'Question' ? 'send' : 'answer',message: item.context})})this.answerTitle = res.info.name.substring(0, 20)}})},enterMessage(event) {if (event.key === 'Enter' && !event.shiftKey) {event.preventDefault()this.sendMessage()}},sendMessage() {// 终止当前对话if (this.isTalking) {this.eventSourceChat && this.eventSourceChat.close()// 关闭后,处理正在对话的"思考中"let curAnswer = this.contentList[this.contentList.length - 1].messagecurAnswer = curAnswer.replaceAll('<div class="think-time">思考中……</div>', '对话中止')this.contentList[this.contentList.length - 1].message = curAnswerthis.isTalking = false// 再获取一下历史记录// 暂时不获取历史记录,因为中止对话时,历史UUID可能还没返回// this.$emit('getHistoryList')return}if (!this.inputMessage ||this.inputMessage.trim() === '' ||this.inputMessage.split(/\r?\n/).every((line) => line.trim() === '')) {this.inputMessage = ''return false}if (!this.choicedTasks.name) {this.$message({ message: '请选择推理任务', type: 'warning' })return false}// 回到初始高度const textarea = this.$refs.inputtextarea.style.height = '56px'this.msgHight = '56px'this.eventSourceChat && this.eventSourceChat.close()// let markedText = this.markdownIt.render(this.inputMessage)this.contentList.push({ type: 'send', message: this.inputMessage })this.contentList.push({ type: 'answer', message: `<div class="think-time">思考中……</div>` })this.answerTitle = this.answerTitle || this.contentList[0].message.substring(0, 20)this.scrollToBottom()this.initSSEChat()},initSSEChat() {const url = `${VSConfig.isHttps ? 'https' : 'http'}://${this.store.leader}/v1/intelligent_computing/task/chat/stream?uuid=${this.choicedTasks.uuid}&message=${encodeURIComponent(this.inputMessage)}&token=${this.store.token}&conversation_uuid=${this.talkUUID}`this.inputMessage = ''this.eventSourceChat = new EventSource(url)let buffer = ''this.startTime = nullthis.endTime = nullthis.thinkTime = nulllet len = this.contentList.lengthlet index = len % 2 === 0 ? len - 1 : lenthis.isTalking = truethis.eventSourceChat.onmessage = async (event) => {await this.sleep(10)if (event.data == '[DONE]') {return false}// 接收 Delta 数据// 最后一条是UUID,第二次发对话的时候要传参try {var { choices, created } = JSON.parse(event.data)} catch (e) {// 新对话在历史列表补充数据this.talkUUID = event.dataif (!this.refreshHistoryFlag) {this.refreshHistoryFlag = event.datathis.$emit('refreshHistory', this.refreshHistoryFlag)}}// const { choices, created } = JSON.parse(event.data)if (choices && choices[0].delta?.content) {buffer += choices[0].delta.content// think标签内是思考内容,单独记录思考时间if (choices[0].delta.content.includes('<think>')) {choices[0].delta.content = `<div class="think-time">思考中……</div><section id="think_content_${index}">`buffer = buffer.replaceAll('<think>', choices[0].delta.content)this.startTime = Math.floor(new Date().getTime() / 1000)}if (choices[0].delta.content.includes('</think>')) {// console.log("结束时间赋值的判断")choices[0].delta.content = `</section>`this.endTime = Math.floor(new Date().getTime() / 1000)// 获取到结束时间后,直接展示收起按钮this.thinkTime = this.endTime - this.startTimebuffer = buffer.replaceAll('<div class="think-time">思考中……</div>',`<div class="think-time">已深度思考(${this.thinkTime}S)<i id="think_icon_${index}" onclick="hiddenThink(${index})" class="ec-font icon-a-xiangxia3"></i></div>`).replaceAll('</think>', choices[0].delta.content).replaceAll(`<section id="think_content_${index}"></section>`, '')}let markedText = this.markdownIt.render(buffer)this.contentList[index] = { type: 'answer', message: markedText }this.scrollToBottomIfAtBottom()}}this.eventSourceChat.onerror = (event) => {console.log('错误触发===》', event)this.contentList[index] = { type: 'answer', message: `<div class="think-time">对话服务连接失败</div>` }this.eventSourceChat.close()this.isTalking = false}this.eventSourceChat.onclose = (event) => {// 关闭事件console.log('关闭事件--->')this.isTalking = false}},sleep(ms) {return new Promise((resolve) => setTimeout(resolve, ms))},scrollToBottomIfAtBottom() {this.$nextTick(() => {const logContainer = this.$refs.logContainerif (logContainer) {const threshold = 100const distanceToBottom = logContainer.scrollHeight - logContainer.scrollTop - logContainer.clientHeightif (distanceToBottom <= threshold) logContainer.scrollTop = logContainer.scrollHeight}})},scrollToBottom() {this.$nextTick(() => {const logContainer = this.$refs.logContainerif (logContainer) {logContainer.scrollTop = logContainer.scrollHeight}})},confirmTask(val) {this.choicedTasks = val},clearWindow() {this.eventSourceChat && this.eventSourceChat.close()this.contentList = []this.answerTitle = ''this.talkUUID = ''this.inputMessage = ''this.refreshHistoryFlag = falsethis.isTalking = falseconst textarea = this.$refs.inputtextarea.style.height = '56px'this.msgHight = '56px'},newChat() {this.clearWindow()this.$emit('clearChat')},adjustInputHeight(event) {// enter键盘按下的换行赋值为空if (event.key === 'Enter' && !event.shiftKey) {this.inputMessage = ''event.preventDefault()return}this.$nextTick(() => {const textarea = this.$refs.inputtextarea.style.height = 'auto'// 最高200pxtextarea.style.height = Math.min(textarea.scrollHeight, 200) + 'px'this.msgHight = textarea.style.height})},copyMsg(txt) {// 复制功能// 创建一个临时的 textarea 元素const textarea = document.createElement('textarea')textarea.value = txt.replace(/<[^>]+>/g, '') // 去掉html标签textarea.style.position = 'fixed'document.body.appendChild(textarea)textarea.select() // 选中文本try {document.execCommand('copy') // 执行复制ElMessage({message: '复制成功',type: 'success'})} catch (err) {ElMessage({message: '复制失败',type: 'error'})} finally {document.body.removeChild(textarea) // 移除临时元素}}},beforeDestroy() {this.eventSourceChat && this.eventSourceChat.close()}
}
</script><style scoped lang="scss">
.talk-window {height: 100%;transition: margin 0.2s ease;position: relative;
}
.talk-container {height: calc(100% - 58px);position: relative;
}
.talk-welcome {text-align: center;// margin-bottom: 25px;padding: 10% 20% 25px;box-sizing: border-box;h1 {margin-bottom: 30px;font-size: 21px;}p {color: #8f9aad;}
}
.messages {padding: 20px;overflow-y: auto;
}.message {display: flex;margin: 12px 0;
}.message.user {justify-content: flex-end;
}.bubble {max-width: 70%;padding: 12px 16px;border-radius: 12px;background: #fff;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}.message.user .bubble {background: #e8f4ff;
}.time {font-size: 12px;color: #666;margin-top: 4px;
}.talk-send {background: #f1f2f7;border-radius: 10px;border: 1px solid #e9e9eb;padding: 5px 10px;margin: 0px 20%;img {cursor: pointer;}textarea {width: 100%;padding: 10px;resize: none;overflow: auto;// min-height: 48px;max-height: 200px;line-height: 1.5;box-sizing: border-box;font-family: inherit;border: 0px;background: #f1f2f7;}textarea:focus {outline: none !important;}
}input {flex: 1;padding: 12px;border: 1px solid #ddd;border-radius: 8px;
}.talk-title {height: 56px;line-height: 56px;p {color: #000000;font-size: 15px;font-weight: 550;text-align: center;}.sangedianBtn {width: 225px;height: 32px;position: absolute;top: 15px;right: 30px;}
}.send-item {max-width: 60%;word-break: break-all;padding: 10px;background: #eef6ff;border-radius: 10px;color: #000000;white-space: pre-wrap;font-size: 13px;
}
.msg-row {margin-bottom: 10px;
}
.talk-box {height: calc(100% - 140px);.talk-content {background-color: #fff;color: #324659;overflow-y: auto;height: calc(100% - 50px);box-sizing: border-box;padding: 0px 20%;// &:hover {// overflow-y: auto;// }.chat-assistant {display: flex;margin-bottom: 10px;.answer-item {line-height: 30px;color: #324659;}}.answer-cont {position: relative;display: flex;width: 100%;> img {width: 30px;height: 30px;margin-right: 10px;}&.end {justify-content: flex-end;}&.start {justify-content: flex-start;}}}.chat-sse {min-height: 100px;max-height: 460px;}.chat-message {height: calc(100vh - 276px);}.thinking-bubble {height: calc(100vh - 296px);}
}
.chat-add {width: 111px;height: 33px;background: #dbeafe;border-radius: 6px !important;font-size: 14px !important;border: 0px;color: #516ffe !important;&:hover {background: #ebf0f7;}.icon-tianjia1 {margin-right: 10px;font-size: 14px;}
}
.talk-btn-cont {text-align: right;height: 30px;margin-top: 5px;
}
</style><style lang="scss">
@use './markdown.scss';
</style>
最终页面:

相关文章:
vue 仿deepseek前端开发一个对话界面
后端:调用deepseek的api,所以返回数据格式和deepseek相同 {"model": "DeepSeek-R1-Distill-Qwen-1.5B", "choices": [{"index": 0, "delta": {"role": "assistant", "cont…...
UE5以插件的形式加载第三方库
之前在UE中加载第三方库的形式是以静态或者动态链接的形式加载但是不太容易复用。就想着能不能以插件的形式加载第三方库,这样直接把插件打包发行就可以复用了,之前也找过相应的教程但是很难找到比较简单易懂的教程,要么是比较复杂࿰…...
Vue3全局化配置(ConfigProvider)
效果如下图: 在线预览 APIs ConfigProvider 参数说明类型默认值theme主题对象Theme{}abstractboolean是否不存在 DOM 包裹元素truetagstringConfigProvider 被渲染成的元素,abstract 为 true 时有效‘div’ Theme Type 名称说明类型默认值common?全…...
Centos7系统基于docker下载ollama部署Deepseek-r1(GPU版不踩坑)
目录 一、Docker下载安装 二、Ollama安装 三、部署Deepseek-R1 一、Docker下载安装 1、更新源 sudo yum update -y 2、下载依赖包 yum install -y yum-utils device-mapper-persistent-data lvm2 3、添加docker远程仓库地址 yum-config-manager --add-repo http://down…...
[目标检测] 训练之前要做什么
背景:训练一个Yolo8模型,在训练之前,数据集的处理是影响效果的关键因素。 Step1 定义规则 什么是人/车,比如人的话可能是站着的人,如果是骑电动车/自行车就不算是人。 Step2 收集数据集 1. 自己标注。如果是自己标…...
写了一个QT的定时器
主程序 #include <QCoreApplication> #include "mainwindow.h"int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);MainWindow w;return a.exec(); }mainwindow.cpp程序 #include "mainwindow.h"#include <QDateTime>MainWi…...
LeetCode 力扣热题100 分割等和子集
题目解析 题目:给定一个正整数数组 nums,判断是否可以将数组分成两个和相等的子集。 等价问题: • 计算 nums 的总和 S • 如果 S 是奇数,直接返回 false(因为不能均分) • 目标是找到一个子集ÿ…...
Hyperlane:轻量级高性能的 Rust Web 后端框架
Hyperlane:开启 Rust Web 开发的新篇章 在当今数字化时代,Web 开发的效率与性能成为了开发者们关注的焦点。随着 Rust 语言的崛起,越来越多的开发者开始探索如何利用 Rust 的高性能和安全性来构建现代 Web 服务。今天,我们非常荣…...
【工具】C#游戏防沉迷小工具
背景介绍 嘿,各位小伙伴!今天想跟大家唠唠我为啥要搞这么个防沉迷小工具。 咱都清楚,现在这游戏啊,玩起来那叫一个带劲,但时间一长,不仅眼睛累,心也跟着累。有些游戏,规则定得挺有意…...
深圳南柯电子|净水器EMC测试整改:水质安全与电磁兼容性的双赢
在当今注重健康生活的时代,净水器作为家庭用水安全的第一道防线,其性能与安全性备受关注。其中,电磁兼容性(EMC)测试是净水器产品上市前不可或缺的一环,它直接关系到产品在复杂电磁环境中的稳定运行及不对其…...
SpeechCraf论文学习
Abstract 核心问题 挑战 语音风格包含细微的多样化信息(如情感、语调、节奏),传统基于标签/模板的标注方法难以充分捕捉,制约了语音-语言多模态模型的性能。 数据瓶颈: 大规模数据收集与高质量标注之间存在矛盾&…...
Work【2】:PGP-SAM —— 无需额外提示的自动化 SAM!
文章目录 前言AbstractIntroductionMethodsContextual Feature ModulationProgressive Prototype RefinementPrototype-based Prompt Generator ExperimentDatasetsImplementation DetailsResults and AnalysisAblation Study 总结 前言 和大家分享一下我们发表在 ISBI 2025 上…...
数据安全之策:备份文件的重要性与自动化实践
在信息化高速发展的今天,数据已成为企业运营和个人生活中不可或缺的重要资源。无论是企业的财务报表、客户资料,还是个人的家庭照片、学习笔记,数据的丢失或损坏都可能带来无法挽回的损失。因此,备份文件的重要性日益凸显…...
uniapp+Vue3 组件之间的传值方法
一、父子传值(props / $emit 、ref / $refs) 1、props / $emit 父组件通过 props 向子组件传递数据,子组件通过 $emit 触发事件向父组件传递数据。 父组件: // 父组件中<template><view class"container">…...
WebSocket生命周期和vue中使用
ing。。。晚点更新 进入页面,生命周期挂载后,window监听ws连接online 正常情况,心跳包检测避免断开 非正常情况,ws.onclose断开, 判断1000状态吗,触发重连函数。 定时器,重连,判断…...
blender使用初体验(甜甜圈教程)
使用blender 建模了甜甜圈,时间空闲了,但愿能创建点好玩的吸引人的东西...
web3区块链
Web3 是指下一代互联网,也被称为“去中心化互联网”或“区块链互联网”。它是基于区块链技术构建的,旨在创建一个更加开放、透明和用户主导的网络生态系统。以下是关于 Web3 的一些关键点: ### 1. **核心概念** - **去中心化**࿱…...
大模型学习笔记------Llama 3模型架构之旋转编码(RoPE)
大模型学习笔记------Llama 3模型架构之旋转编码(RoPE) 1、位置编码简介1.1 绝对位置编码1.2 相对位置编码 2、旋转编码(RoPE)2.1 基本概念---旋转矩阵2.2 RoPE计算原理2.2.1 绝对位置编码2.2.2 相对位置编码 3、旋转编码…...
04 1个路由器配置一个子网的dhcp服务
前言 这是最近一个朋友的 ensp 相关的问题, 这里来大致了解一下 ensp, 计算机网络拓扑 相关基础知识 这里一系列文章, 主要是参照了这位博主的 ensp 专栏 这里 我只是做了一个记录, 自己实际操作了一遍, 增强了一些 自己的理解 当然 这里仅仅是一个 简单的示例, 实际场景…...
安装open-webui
open-webui是一个开源的大语言模型交互界面 前提:Ollama已安装,并下载了deepseek-r1:1.5b模型 拉取镜像 docker pull ghcr.io/open-webui/open-webui:main 配置docker-compose.yml services:open-webui:image: ghcr.io/open-webui/open-webui:mainv…...
HCIA-11.以太网链路聚合与交换机堆叠、集群
链路聚合背景 拓扑组网时为了高可用,需要网络的冗余备份。但增加冗余容易后会出现环路,所以我们部署了STP协议来破除环路。 但是,根据实际业务的需要,为网络不停的增加冗余是现实需要的一部分。 那么,为了让网络冗余…...
不与最大数相同的数字之和(信息学奥赛一本通-1113)
【题目描述】 输出一个整数数列中不与最大数相同的数字之和。 【输入】 输入分为两行: 第一行为N(N为接下来数的个数,N < 100); 第二行N个整数,数与数之间以一个空格分开,每个整数的范围是-1000,000到1000,000。 【…...
Blender学习方法与技巧
以下是针对Blender零基础用户的学习教程推荐与高效学习方法总结,结合了多个优质资源整理而成,帮助快速入门: 一、Blender学习方法与技巧 制定学习计划与目标 明确短期目标(如掌握基础操作)和长期目标(如独立…...
Amazon RDS ProxySQL 探索(一)
:::info 💡 在日常开发中,开发者们会涉及到数据库的连接,在使用Amazon RDS数据库时,若使用集群模式或者多数据库时,会出现一写多读多个Endpoint,在实际开发中, 开发者们配置数据库连接通常希望走…...
HTML嵌入CSS样式超详解(尊享)
一、行内样式(Inline CSS) 1. 定义与语法 行内样式是直接在HTML标签中使用style属性来定义样式。这种方式只对当前标签生效。 <tagname style"css 样式">2. 示例 <p style"color: red; font-size: 14px;">这是一个红…...
[C语言基础]13.动态内存管理
动态内存管理 1. 动态内存分配2. 动态内存函数的介绍2.1 malloc2.2 free2.3 calloc2.4 realloc 3. 动态内存错误3.1 NULL指针解引用3.2 动态开辟空间越界访问3.3 非动态开辟内存使用free释放3.4 free释放动态开辟内存的一部分3.5 同一块动态内存多次释放3.6 动态开辟内存未释放…...
200多种算法应用于二维和三维无线传感器网络(WSN)覆盖场景
2.1 二元感知模型 在当前无线传感器网络(WSN)覆盖场景中,最常见且理想的感知模型是二元感知模型[27]。如图2所示, Q 1 Q_1 Q1和 Q 2 Q_2 Q2代表平面区域内的两个随机点。 Q 1 Q_1 Q1位于传感器的检测区域内,其感…...
模拟类似 DeepSeek 的对话
以下是一个完整的 JavaScript 数据流式获取实现方案,模拟类似 DeepSeek 的对话式逐段返回效果。包含前端实现、后端模拟和详细注释: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><titl…...
鸿蒙(OpenHarmony)开发实现 息屏/亮屏 详情
官网参考链接 实现点击关闭屏幕,定时5秒后唤醒屏幕 权限 {"name": "ohos.permission.POWER_OPTIMIZATION"}代码实现 import power from ohos.power;Entry Component struct Page3 {private timeoutID: number | null null; // 初始化 timeout…...
Flutter PopScope对于iOS设置canPop为false无效问题
这个问题应该出现很久了,之前的组件WillPopScope用的好好的,flutter做优化打算“软性”处理禁用返回手势,出了PopScope,这个组件也能处理在安卓设备上的左滑返回事件。但是iOS上面左滑返回手势禁用,一直无效。 当然之…...
