vue3+elementPlus pc和小程序ai聊天文生图

websocket封装可以看上一篇文章
在这里插入图片描述

//pc端
<template><div class="common-layout theme-white"><el-container><el-aside><div class="title-box"><span>AI Chat</span></div><div class="chat-list"><!-- <div class="search-box"><el-input v-model="chatName" class="w-50 m-2" size="small" placeholder="搜索会话" @keyup="searchChat"><template #prefix><el-icon class="el-input__icon"><Search /></el-icon></template></el-input></div> --><div class="content" :style="{ height: leftBoxHeight + 'px' }"><el-row v-for="chat in chatList" :key="chat.chat_id"><div :class="chat.chat_id === activeChat.chat_id ? 'chat-list-item active' : 'chat-list-item'"@click="changeChat(chat)"><el-image :src="chat.icon" class="avatar" /><span class="chat-title-input" v-if="chat.edit"><el-input v-model="tmpChatTitle" size="small" @keydown="titleKeydown($event, chat)"placeholder="请输入会话标题" /></span><span v-else class="chat-title">{{ chat.title }}</span><span class="btn btn-check" v-if="chat.edit || chat.removing"><el-icon @click="confirm($event, chat)"><Check /></el-icon><el-icon @click="cancel($event, chat)"><Close /></el-icon></span><span class="btn" v-else><el-icon title="编辑" @click="editChatTitle($event, chat)"><Edit /></el-icon><el-icon title="删除会话" @click="removeChat($event, chat)"><Delete /></el-icon></span></div></el-row></div></div><div class="tool-box"><el-dropdown :hide-on-click="true" class="user-info" trigger="click"><span class="el-dropdown-link"><el-image src="/images/logo.png" /><span class="username">{{ phoneNumber }}</span><el-icon><ArrowDown /></el-icon></span><template #dropdown><el-dropdown-menu style="width: 296px;"><el-dropdown-item @click="showConfig"><el-icon><Tools /></el-icon><span>修改密码</span></el-dropdown-item><!-- <el-dropdown-item @click="clearAllChats"><el-icon><Delete /></el-icon><span>清除所有会话</span></el-dropdown-item> --><el-dropdown-item @click="logout"><i class="iconfont icon-logout"></i><span>注销</span></el-dropdown-item></el-dropdown-menu></template></el-dropdown></div></el-aside><el-main v-loading="loading" element-loading-background="rgba(122, 122, 122, 0.3)"element-loading-text="正在生成..."><div class="chat-head"><div class="chat-config"><span class="role-select-label">选择类型:</span><el-select v-model="roleId" filterable placeholder="类型" class="role-select" @change="newChat"><el-option v-for="item in roles" :key="item.id" :label="item.name" :value="item.id"><div class="role-option"><el-image :src="item.icon"></el-image><span>{{ item.name }}</span></div></el-option></el-select><!-- <el-select v-model="modelID" placeholder="模型" @change="newChat"><el-option v-for="item in models" :key="item.id" :label="item.name" :value="item.id" /></el-select> --><el-button type="primary" @click="newChat"><el-icon><Plus /></el-icon>新建对话</el-button><!-- <el-button type="success" @click="exportChat" plain><i class="iconfont icon-export"></i><span>导出会话</span></el-button> --><!-- <el-button type="warning" @click="showFeedbackDialog = true"><el-icon><Promotion /></el-icon><span>意见反馈</span></el-button> --></div></div><div class="right-box" :style="{ height: mainWinHeight + 'px' }"><div><div id="container"><div class="chat-box" id="chat-box" :style="{ height: chatBoxHeight + 'px' }"><!-- <div v-if="showHello"> --><!-- <welcome @send="autofillPrompt" /> --><!-- </div> --><div v-for="item in chatData" :key="item.id"><chat-prompt v-if="item.type === 'prompt'" :icon="item.icon":created-at="dateFormat(item['created_at'])" :tokens="item['tokens']" :model="getModelValue(modelID)":content="item.content" /><chat-reply v-else-if="item.type === 'reply'" :icon="item.icon" :org-content="item.orgContent":created-at="dateFormat(item['created_at'])" :tokens="item['tokens']" :content="item.content" /><chat-mid-journey v-else-if="item.type === 'mj'" :content="item.content" :role-id="item.role_id":chat-id="item.chat_id" :icon="item.icon" @disable-input="disableInput(true)"@enable-input="enableInput" :created-at="dateFormat(item['created_at'])" /></div></div><div class="re-generate"><div class="btn-box"><el-button type="info" v-if="showStopGenerate" @click="stopGenerate" plain><el-icon><VideoPause /></el-icon>停止生成</el-button><el-button type="primary" v-if="showReGenerate" @click="reGenerate" plain><el-icon><RefreshRight /></el-icon>重新生成</el-button></div></div><div class="input-box"><div class="input-container"><el-input ref="textInput" v-model="prompt" v-on:keydown="inputKeyDown" autofocus type="textarea":rows="2" placeholder="按 Enter 键发送消息,使用 Ctrl + Enter 换行" /><!-- <span class="send-btn" style="right: 50px;"><el-button @click="sendImage"><el-icon><PictureFilled /></el-icon></el-button></span> --><span class="send-btn"><el-button @click="sendMessage"><el-icon><Promotion /></el-icon></el-button></span></div></div><!-- end input box --></div><!-- end container --></div><!-- end loading --></div></el-main></el-container><!--账户信息 --><PasswordDialog :show="showConfigDialog" @hide="showConfigDialog = false" /><!-- 上传图片 --><el-dialog class="config-dialog" v-model="showimgUpload" :close-on-click-modal="true" :before-close="close"style="width:60%" title="上传图片"><el-upload class="avatar-uploader" :auto-upload="true" :before-upload="beforeUpload" :show-file-list="false":http-request="afterRead" accept="image/png, image/jpeg,image/jpg"><img v-if="imageUrl" :src="imageUrl" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload><template #footer><span class="dialog-footer"><el-button type="primary" @click="handleConfirm">确定</el-button></span></template></el-dialog></div>
</template>
<script setup>
import { nextTick, onMounted, ref } from 'vue'
import ChatPrompt from "@/components/ChatPrompt.vue";
import ChatReply from "@/components/ChatReply.vue";
import {ArrowDown,Check,Close,Delete,Edit,Plus,Promotion,RefreshRight,Search,Tools,VideoPause,PictureFilled
} from '@element-plus/icons-vue'
import 'highlight.js/styles/a11y-dark.css'
import { dateFormat, isMobile, randString, removeArrayItem, UUID } from "@/utils/libs";
import { ElMessage, ElMessageBox } from "element-plus";
import hl from "highlight.js";
import { getSessionId, getUserToken, removeUserToken } from "@/store/session";
import { httpGet, httpPost } from "@/utils/http";
import { useRouter } from "vue-router";
import Clipboard from "clipboard";
import PasswordDialog from "@/components/PasswordDialog.vue";
import imgUpload from "@/components/imgUpload.vue";
import { checkSession } from "@/action/session";
import Welcome from "@/components/Welcome.vue";
import ChatMidJourney from "@/components/ChatMidJourney.vue";
import { createWebscoket, onSend, closeWs, start, onMessage } from '@/utils/socket.js'
import { chineseChar2englishChar } from "@/utils/validate";
import Compressor from "compressorjs";const title = ref('ChatGPT-智能助手');
const models = ref([])
const modelID = ref(0)
const chatData = ref([]);
const allChats = ref([]); // 会话列表
const chatList = ref(allChats.value);
const activeChat = ref({});
const mainWinHeight = ref(0); // 主窗口高度
const chatBoxHeight = ref(0); // 聊天内容框高度
const leftBoxHeight = ref(0);
const loading = ref(false);
const loginUser = ref(null);
const roles = ref([{"id": 1,"created_at": 0,"updated_at": 0,"key": "gpt","name": "文生图","context": null,"hello_msg": "您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。","icon": "/images/avatar/gpt.png","enable": true,"sort": 1
}]);
const roleId = ref(1)
const newChatItem = ref(null);
const router = useRouter();
const showConfigDialog = ref(false);
const showimgUpload = ref(false);
const isLogin = ref(false)
const showHello = ref(true)
const textInput = ref(null)
const showFeedbackDialog = ref(false)
const showDemoNotice = ref(false)
const showNoticeKey = ref("SHOW_DEMO_NOTICE_")
const socketMsg = ref(null)
const imageUrl = ref(null)
const token = ref("")
const openId = ref("")
const phoneNumber = ref('')
const password = ref('')
if (isMobile()) {router.replace("/mobile")
}onMounted(() => {resizeElement();token.value = localStorage.getItem('token')openId.value = localStorage.getItem('openId')phoneNumber.value = localStorage.getItem('phoneNumber')password.value = localStorage.getItem('password')models.value = {phoneNumber: phoneNumber.value,password: password.value}const clipboard = new Clipboard('.copy-reply, .copy-code-btn');clipboard.on('success', () => {ElMessage.success('复制成功!');})clipboard.on('error', () => {ElMessage.error('复制失败!');})if (token.value) {let host = process.env.VUE_APP_WS_HOSTcreateWebscoket(host + token.value, messagesCallBack)chatData.value = []; // 初始化聊天数据previousText.value = '';//上一次提问enableInput()activelyClose.value = false;//主动关闭chatData.value.push({content: '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。',type: "reply",id: randString(32),icon: '/images/logo.png',orgContent: '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。',})window.onresize = () => resizeElement();} else {router.push('/login')}});
const getRoleById = function (rid) {for (let i = 0; i < roles.value.length; i++) {if (roles.value[i]['id'] === rid) {return roles.value[i];}}return null;
}
//新消息监听
const messagesCallBack = (msg) => {let socketMsg = msg.msg.replace(/\ufeff/g, "");let socketMsgTwo = JSON.parse(socketMsg)if (socketMsgTwo.msgType != -1 || socketMsgTwo.msgType == 200) {loading.value = trueif (socketMsgTwo.contentUrl) {chatData.value.push({type: "mj",key: randString(32),icon: '/images/logo.png',content: socketMsgTwo.contentUrl})loading.value = false}// 将聊天框的滚动条滑动到最底部nextTick(() => {document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)})enableInput()}
}
//发送socket消息到服务器
const sendMessagext = (data) => {onSend(JSON.stringify(data))
}
//断开socket
const closeWsTxt = () => {closeWs()
}const prompt = ref('');
const showStopGenerate = ref(false); // 停止生成
const showReGenerate = ref(false); // 重新生成
const previousText = ref(''); // 上一次提问
const lineBuffer = ref(''); // 输出缓冲行
const activelyClose = ref(false); // 主动关闭
const canSend = ref(true);// 发送消息
const sendMessage = function () {console.log(prompt.value)start()if (canSend.value === false) {ElMessage.warning("AI 正在作答中,请稍后...");return}if (prompt.value.trim().length === 0 || canSend.value === false) {return false;}else{loading.value = true}// 追加消息chatData.value.push({content: md.render(prompt.value),type: "prompt",id: randString(32),icon: '',created_at: new Date().getTime(),});nextTick(() => {document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)})showHello.value = falsedisableInput(false)// socket.value.send(prompt.value);var data = {userId: openId.value,toUserId: openId.value,content: chineseChar2englishChar(prompt.value),msgType: 2}//websocket 发送sendMessagext(data)previousText.value = prompt.value;//上一次tiwenprompt.value = '';return true;
}//发送图片
const sendImage = function () {showimgUpload.value = true
}
//上传前
const beforeUpload = (file) => {if (file.type.indexOf("image") < 0) {ElMessage.error('请上传正确的图片格式')return false;}
};
//上传后
const afterRead = (file) => {// 压缩图片并上传new Compressor(file.file, {quality: 0.6,success(result) {const formData = new FormData();formData.append('file', result, result.name);// 执行上传操作httpPost('/api/upload', formData).then((res) => {user.value.avatar = res.dataElMessage.success({ message: "上传成功", duration: 500 })}).catch((e) => {ElMessage.error('图片上传失败:' + e.message)})},error(err) {console.log(err.message);},});
};
//图片确定
const handleConfirm = () => {showimgUpload.value = false
}
const resizeElement = function () {chatBoxHeight.value = window.innerHeight - 51 - 82 - 38;mainWinHeight.value = window.innerHeight - 51;leftBoxHeight.value = window.innerHeight - 43 - 47 - 45;
};// 新建会话
const newChat = function () {// 获取当前聊天角色图标let icon = '';roles.value.forEach(item => {if (item['id'] === roleId.value) {icon = item['icon']}})newChatItem.value = {chat_id: "",icon: icon,role_id: roleId.value,model_id: modelID.value,title: '',edit: false,removing: false,};activeChat.value = {} //取消激活的会话高亮showStopGenerate.value = false;showReGenerate.value = false;connect(null, roleId.value)
}const titleKeydown = (e, chat) => {if (e.keyCode === 13) {e.stopPropagation();confirm(e, chat)}
}const md = require('markdown-it')({breaks: true,highlight: function (str, lang) {const codeIndex = parseInt(Date.now()) + Math.floor(Math.random() * 10000000)// 显示复制代码按钮const copyBtn = `<span class="copy-code-btn" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span>
<textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy-target-${codeIndex}">${str.replace(/<\/textarea>/g, '&lt;/textarea>')}</textarea>`if (lang && hl.getLanguage(lang)) {const langHtml = `<span class="lang-name">${lang}</span>`// 处理代码高亮const preCode = hl.highlight(lang, str, true).value// 将代码包裹在 pre 中return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn} ${langHtml}</pre>`}// 处理代码高亮const preCode = md.utils.escapeHtml(str)// 将代码包裹在 pre 中return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn}</pre>`}
});
// 取消修改
const cancel = function (event, chat) {event.stopPropagation();chat.edit = false;chat.removing = false;
}const disableInput = (force) => {canSend.value = false;showReGenerate.value = false;showStopGenerate.value = !force;
}const enableInput = () => {canSend.value = true;showReGenerate.value = previousText.value !== "";showStopGenerate.value = false;
}
// 自动填充 prompt
const autofillPrompt = (text) => {prompt.value = texttextInput.value.focus()// sendMessage()
}
// 登录输入框输入事件处理
const inputKeyDown = function (e) {if (e.keyCode === 13) {if (e.ctrlKey) { // Ctrl + Enter 换行prompt.value += "\n";return;}e.preventDefault();sendMessage();}
}//账户信息
const showConfig = function () {showConfigDialog.value = true;
}const logout = function () {activelyClose.value = true;localStorage.clear();router.push('/login');ElMessage.success('退出成功');
}const stopGenerate = function () {showStopGenerate.value = false;httpGet("/api/chat/stop?session_id=" + getSessionId()).then(() => {enableInput()})
}// 重新生成
const reGenerate = function () {disableInput(false)const text = '重新生成上述问题的答案:' + previousText.value;// 追加消息loading.value=truechatData.value.push({type: "prompt",id: randString(32),icon: '/images/avatar/mid_journey.png',content: md.render(text)});var data = {userId: openId.value,toUserId: openId.value,content: chineseChar2englishChar(prompt.value),msgType: 2}sendMessagext(data)}</script><style scoped lang="stylus">
@import "@/assets/css/chat-plus.styl"
</style>
<style>
.avatar-uploader .el-upload {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;
}.avatar-uploader .el-upload:hover {border-color: #409EFF;
}.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;line-height: 178px;text-align: center;
}.avatar {width: 178px;height: 178px;display: block;
}</style>

在这里插入图片描述

//移动端
<template><div class="app-background"><van-config-provider theme="dark"><div class="mobile-chat"><van-sticky ref="navBarRef" :offset-top="0" position="top"><!-- <van-nav-bar left-arrow left-text="返回" @click-left="router.back()"> --><!-- <template #title><van-dropdown-menu><van-dropdown-item :title="title"><van-cell center title="角色"> {{ role.name }}</van-cell><van-cell center title="模型">{{ modelValue }}</van-cell></van-dropdown-item></van-dropdown-menu></template> --><!-- <template #right><van-icon name="share-o" @click="showShare = true"/></template> --><!-- </van-nav-bar> --></van-sticky><!-- <van-share-sheet v-model:show="showShare" title="立即分享给好友" :options="shareOptions" @select="shareChat" /> --><div class="chat-list-wrapper"><div id="message-list-box" :style="{ height: winHeight + 'px' }" class="message-list-box"><van-list v-model:error="error" :finished="finished" error-text="请求失败,点击重新加载" @load="onLoad"><van-cell v-for="item in chatData" :key="item" :border="false" class="message-line"><chat-prompt v-if="item.type === 'prompt'" :content="item.content":created-at="dateFormat(item['created_at'])" :icon="item.icon" :model="model":tokens="item['tokens']" /><chat-reply v-else-if="item.type === 'reply'" :content="item.content":created-at="dateFormat(item['created_at'])" :icon="item.icon" :org-content="item.orgContent":tokens="item['tokens']" /><chat-mid-journey v-else-if="item.type === 'mj'" :content="item.content" :icon="item.icon" :role-id="role":chat-id="chatId" @disable-input="disableInput(true)" @enable-input="enableInput":created-at="dateFormat(item['created_at'])" /></van-cell></van-list></div></div><div class="chat-box-wrapper"><van-sticky ref="bottomBarRef" :offset-bottom="0" position="bottom"><van-cell-group inset><van-field v-model="prompt" center clearable placeholder="输入你的问题"><template #button><van-button size="small" type="primary" @click="sendMessage">发送</van-button></template><template #extra><div class="icon-box" style="margin-left: 10rpx;"><van-icon v-if="showStopGenerate" name="stop-circle-o" @click="stopGenerate" /><van-icon v-if="showReGenerate" name="play-circle-o" @click="reGenerate" /></div></template></van-field></van-cell-group></van-sticky></div></div></van-config-provider><van-overlay z-index="100" :show="loading" class="wrapper"><van-loading vertical class="block"><template #icon><van-icon name="star-o" size="30" /></template>正在生成...</van-loading></van-overlay></div>
</template><script setup>
import { nextTick, onMounted, ref } from "vue";
import { showToast } from "vant";
import { useRouter } from "vue-router";
import { dateFormat, randString, renderInputText, UUID } from "@/utils/libs";
import { getChatConfig } from "@/store/chat";
import { httpGet } from "@/utils/http";
import hl from "highlight.js";
import 'highlight.js/styles/a11y-dark.css'
import ChatPrompt from "@/components/mobile/ChatPrompt.vue";
import ChatReply from "@/components/mobile/ChatReply.vue";
import { getSessionId, getUserToken } from "@/store/session";
import { checkSession } from "@/action/session";
import { getMobileTheme } from "@/store/system";
import ChatMidJourney from "@/components/mobile/ChatMidJourney.vue";import QRCode from "qrcode";
import { ElMessage } from "element-plus";
import Clipboard from "clipboard";
import InviteList from "@/components/InviteList.vue";import { createWebscoket, onSend, closeWs, start, onMessage } from '@/utils/socket.js'
import { chineseChar2englishChar } from "@/utils/validate";const winHeight = ref(0)
const navBarRef = ref(null)
const bottomBarRef = ref(null)
const router = useRouter()const chatConfig = getChatConfig()
const role = chatConfig ? chatConfig.role : ''
const model = chatConfig ? chatConfig.model : ''
const modelValue = chatConfig ? chatConfig.modelValue : ""
const title = chatConfig ? chatConfig.title : ''
const chatId = chatConfig ? chatConfig.chatId : ''
const loginUser = ref(null)const listBoxHeight = window.innerHeight
const inviteURL = ref("")
const qrImg = ref("")
const inviteChatCalls = ref(0)
const inviteImgCalls = ref(0)
const hits = ref(0)
const regNum = ref(0)
const rate = ref(0)
const isLogin = ref(false)
const token = ref("")
const openId = ref("")
const overlayshow = ref(false)onMounted(() => {var url = window.location.href;console.log(url)var regex = /[?&]token=([^&#]+)/; // 匹配 ? 或 & 后面跟 token= 开头的部分var regexId = /[?&]openId=([^&#]+)/; // 匹配 ? 或 & 后面跟 token= 开头的部分var match = url.match(regex);token.value = decodeURIComponent(match[1]);openId.value = decodeURIComponent(url.match(regexId)[1]);localStorage.setItem('token', token.value)localStorage.setItem('openId', openId.value)winHeight.value = document.body.offsetHeight - navBarRef.value.$el.offsetHeight - bottomBarRef.value.$el.offsetHeightlet host = process.env.VUE_APP_WS_HOSTcreateWebscoket(host + token.value, messagesCallBack)loading.value = falsepreviousText.value = '';canSend.value = true;activelyClose.value = false;chatData.value.push({content: '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。',type: "reply",id: randString(32),icon: '/images/logo.png',orgContent: '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。',})
})const chatData = ref([])
const loading = ref(false)
const finished = ref(false)
const error = ref(false)checkSession().then(user => {loginUser.value = user
}).catch(() => {router.push('/mobile')
})
//取url中的参数值
const getQuery = (name) => {// 正则:[找寻'&' + 'url参数名字' = '值' + '&']('&'可以不存在)let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");let r = window.locationconsole.log(r);if (r != null) {// 对参数值进行解码return decodeURIComponent(r[2]);}return null;
}//新消息监听
const messagesCallBack = (msg) => {let socketMsg = msg.msg.replace(/\ufeff/g, "");let socketMsgTwo = socketMsg ? JSON.parse(socketMsg) : ''if (socketMsgTwo.msgType != -1 || socketMsgTwo.msgType == 200) {loading.value = trueif (socketMsgTwo.contentUrl) {chatData.value.push({type: "mj",key: randString(32),icon: '/images/logo.png',content: socketMsgTwo.contentUrl})loading.value = false}// 将聊天框的滚动条滑动到最底部nextTick(() => {document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)// localStorage.setItem("chat_id", chat_id)})enableInput()}
}//发送socket消息到服务器
const sendMessagext = (data) => {onSend(JSON.stringify(data))
}
//断开socket
const closeWsTxt = () => {closeWs()
}// 创建 socket 连接
const prompt = ref('');
const showStopGenerate = ref(false); // 停止生成
const showReGenerate = ref(false); // 重新生成
const previousText = ref(''); // 上一次提问
const lineBuffer = ref(''); // 输出缓冲行
const socket = ref(null);
const activelyClose = ref(false); // 主动关闭
const canSend = ref(true);
const connect = function () {let host = process.env.VUE_APP_WS_HOSTcreateWebscoket(host + token.value, messagesCallBack)loading.value = falsepreviousText.value = '';canSend.value = true;activelyClose.value = false;chatData.value.push({content: '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。',type: "reply",id: randString(32),icon: '/images/logo.png',orgContent: '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。',})}const disableInput = (force) => {canSend.value = false;// loading.value = true;showReGenerate.value = false;showStopGenerate.value = !force;
}const enableInput = () => {canSend.value = true;// loading.value = false;showReGenerate.value = previousText.value !== "";showStopGenerate.value = false;
}// 将聊天框的滚动条滑动到最底部
const scrollListBox = () => {document.getElementById('message-list-box').scrollTo(0, document.getElementById('message-list-box').scrollHeight + 46)
}const sendMessage = () => {if (canSend.value === false) {showToast("AI 正在作答中,请稍后...");return}if (prompt.value.trim().length === 0) {return false;}else{loading.value = true}// 追加消息chatData.value.push({type: "prompt",id: randString(32),icon: '',content: renderInputText(prompt.value),created_at: new Date().getTime(),});nextTick(() => {scrollListBox()})disableInput(false)// socket.value.send(prompt.value);var data = {userId: openId.value,toUserId: openId.value,content: chineseChar2englishChar(prompt.value),msgType: 2}//websocket 发送sendMessagext(data)previousText.value = prompt.value;prompt.value = '';return true;
}const stopGenerate = () => {showStopGenerate.value = false;enableInput()}const reGenerate = () => {disableInput(false)const text = '重新生成上述问题的答案:' + previousText.value;// 追加消息chatData.value.push({type: "prompt",id: randString(32),icon: '',content: renderInputText(text)});var data = {userId: openId.value,toUserId: openId.value,content: chineseChar2englishChar(prompt.value),msgType: 1}// socket.value.send(text);sendMessagext(data)
}const showShare = ref(false)
const shareOptions = [{ name: '微信', icon: 'wechat' },{ name: '微博', icon: 'weibo' },{ name: '复制链接', icon: 'link' },{ name: '分享海报', icon: 'poster' },
]
const shareChat = () => {showShare.value = falserouter.push('/mobile/Invitation');
}
</script><style lang="stylus" scoped>
@import "@/assets/css/mobile/chat-session.css"
</style>
<style>.wrapper {display: flex;align-items: center;justify-content: center;height: 100%;}.block {width: 120px;height: 120px;font-size: 20px;color: #1989fa;/* background-color: #fff; */}
</style>
//uniapp
<template><view class="main"><u-navbar :fixed="true" :autoBack="false" @leftClick="goBack"></u-navbar><web-view :src="url" @message="message"></web-view></view>
</template>
<script>export default {data() {return {url: '',token: '',canBack: false,openId: ''}},onLoad() {this.token = uni.getStorageSync('token');this.openId = uni.getStorageSync('openid');this.url = 'https://xxxxx.com/mobile?token=' + encodeURIComponent(this.token) + '&openId=' +encodeURIComponent(this.openId)console.log(this.url)},methods: {message(event) {console.log(event.detail.data);},},}
</script>
<style>.main {width: 100%;height: 100vh;}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/246453.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【软件测试】学习笔记-构建并执行 JMeter 脚本的正确姿势

有些团队在组建之初往往并没有配置性能测试人员&#xff0c;后来随着公司业务体量的上升&#xff0c;开始有了性能测试的需求&#xff0c;很多公司为了节约成本会在业务测试团队里选一些技术能力不错的同学进行性能测试&#xff0c;但这些同学也是摸着石头过河。他们会去网上寻…

一天吃透计算机网络面试八股文

面试网站&#xff1a;topjavaer.cn 目录&#xff1a; 网络分层结构三次握手两次握手可以吗&#xff1f;四次挥手第四次挥手为什么要等待2MSL&#xff1f;为什么是四次挥手&#xff1f;TCP有哪些特点&#xff1f;说说TCP报文首部有哪些字段&#xff0c;其作用又分别是什么&…

idea结合git回到某个提交点

概述&#xff1a;在IntelliJ IDEA中&#xff0c;你可以使用Git工具来回到某个提交点。 第一步&#xff1a;打开idea&#xff0c;打开git的管理面 可以看到&#xff0c;由于我的大改动&#xff0c;导致现在出问题了&#xff0c;所以我准备回退到某一版本。 点击左下角的git 点…

使用IntelliJ IDEA快速搭建springboot 基础模板项目

使用IntelliJ IDEA快速搭建springboot 基础模板项目&#xff01;今天和大家分享一下&#xff0c;如何使用IntelliJ IDEA里面的maven插件&#xff0c;来快速搭建一个简单的Springboot基础项目。 第一步&#xff0c;菜单里面找到&#xff0c;文件-》新建-项目。如图。我们勾选了是…

这是一片测试文章

这是一片测试文章 这是一片测试文章 这是一片测试文章 这是一片测试文章 这是一片测试文章 这是一片测试文章 真的是测试文章 -111122225555444433333333222211111 dddddaaa

sklearn 学习-混淆矩阵 Confusion matrix

混淆矩阵Confusion matrix&#xff1a;也称为误差矩阵&#xff0c;通过计算得出矩阵的结果用来表示分类器的精度。其每一列代表预测值&#xff0c;每一行代表的是实际的类别。 from sklearn.metrics import confusion_matrixy_true [2, 0, 2, 2, 0, 1] y_pred [0, 0, 2, 2, 0…

高中数学:集合

一、基本概念与关系 1、元素 2、集合 集合中元素的特性 1、确定性。2、无序性。3、互异性。 3、空集&#xff0c;用∅符号表示 4、元素与集合的关系是属于关系&#xff0c;用∈符号表示 5、集合与集合的关系是包含关系。用⊆或者⊊符号表示 子集与真子集。 A⊊B > A⊆B 反之…

Microsoft Remote Desktop for Mac(远程桌面连接)激活版

Microsoft Remote Desktop是一款由微软开发的远程桌面连接工具&#xff0c;它允许用户从另一台计算机或移动设备远程连接到Windows桌面或服务器。 以下是该软件的一些主要特点和功能&#xff1a; 跨平台支持&#xff1a;Microsoft Remote Desktop支持Windows、macOS、iOS和Andr…

数据结构(顺序表)

文章目录 一、线性表1、线性表1.1、线性表的定义1.2、线性表的操作 2、顺序表2.1、顺序表的实现--静态分配2.2、顺序表的实现--动态分配2.2、顺序表的特点 3、顺序表的基本操作3.1、插入操作3.2、删除操作3.3、查找操作3.2、按位查找3.2、按值查找 一、线性表 1、线性表 1.1、…

第二百八十八回

文章目录 1. 概念介绍2. 使用方法2.1 实现步骤2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取文件类型"相关的内容&#xff0c;本章回中将介绍如何播放视频.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 播放视频是我们常用…

Docker Registry(镜像仓库)

什么是Docker Registry 镜像仓库负责存储&#xff0c;管理和分发镜像&#xff0c;并且提供登入认证能力&#xff0c;建立仓库的索引。镜像仓库管理多个repositoy&#xff0c;repositoy通过命名来区分。每个repository包含一个或多个镜像&#xff0c;镜像通过镜像名称和标签&am…

k8s从初识到上天系列第二篇:kubernetes的组件和架构

&#x1f609;&#x1f609; 欢迎加入我们的学习交流群呀&#xff01; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring、SpringSecurity、Docker、Grpc、各种MQ、Rpc、SpringCloud等等很多应用和源码…

【JAVA语言-第15话】集合框架(二)——List、ArrayList、LinkedList、Vector集合

目录 List集合 1.1 概述 1.2 特点 1.3 常用方法 1.4 ArrayList集合 1.4.1 概述 1.4.2 练习 1.5 LinkedList集合 1.5.1 概述 1.5.2 特点 1.5.3 常用方法 1.5.4 练习 1.6 Vector类 1.6.1 概述 1.6.2 练习 1.7 List实现类的异同点 List集合 1.1 概述 java.util…

【自动化测试】读写64位操作系统的注册表

自动化测试经常需要修改注册表 很多系统的设置&#xff08;比如&#xff1a;IE的设置&#xff09;都是存在注册表中。 桌面应用程序的设置也是存在注册表中。 所以做自动化测试的时候&#xff0c;经常需要去修改注册表 Windows注册表简介 注册表编辑器在 C:\Windows\regedit…

一篇文章带你了解C++中隐含的this指针

文章目录 一、this指针的引出二、this指针的特性【面试题】 一、this指针的引出 我们先来定义一个日期类Date&#xff0c;下面这段代码执行的结果是什么呢&#xff1f; class Date { public:void Init(int year, int month, int day){_year year;_month month;_day day;}v…

基于springboot+vue的明星周边产品销售网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

Python tkinter (6) Listbox

Python的标准Tk GUI工具包的接口 tkinter系列文章 python tkinter窗口简单实现 Python tkinter (1) —— Label标签 Python tkinter (2) —— Button标签 Python tkinter (3) —— Entry标签 Python tkinter (4) —— Text控件 GUI 目录 Listbox 创建listbox 添加元素…

AI数字人-数字人视频创作数字人直播效果媲美真人

在科技的不断革新下&#xff0c;数字人技术正日益融入到人们的生活中。近年来&#xff0c;随着AI技术的进一步发展&#xff0c;数字人视频创作领域出现了一种新的创新方式——AI数字人。数字人视频通过AI算法生成虚拟主播&#xff0c;其外貌、动作、语音等方面可与真实人类媲美…

09. Springboot集成sse服务端推流

目录 1、前言 2、什么是SSE 2.1、技术原理 2.2、SSE和WebSocket 2.2.1、SSE (Server-Sent Events) 2.2.2、WebSocket 2.2.3、选择 SSE 还是 WebSocket&#xff1f; 3、Springboot快速集成 3.1、添加依赖 3.2、创建SSE控制器 3.2.1、SSEmitter创建实例 3.2.2、SSEmi…

C++:引用

目录 概念&#xff1a; 引用的使用格式&#xff1a; 引用特性&#xff1a; 常引用 使用场景&#xff1a; 1、做参数 二级指针时的取别名 一级指针取别名 一般函数取别名 2、做返回值 函数返回值的原理&#xff1a; 引用的返回值使用&#xff1a; 引用和指针的对比&…