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.mzph.cn/news/648329.shtml

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

相关文章

iOS推送通知

文章目录 一、推送通知的介绍1. 简介2. 通知的分类 二、本地通知1. 本地通知的介绍2. 实现本地通知3. 监听本地通知的点击 三、远程通知1. 什么是远程通知2. 为什么需要远程通知3. 远程通知的原理4. 如何做远程通知5. 远程通知证书配置6. 获取远程推送要用的 DeviceToken7. 测试…

代码随想录算法训练营第30天 | 回溯总结 + 3道Hard题目

今日任务 332.重新安排行程 51. N皇后 37. 解数独 总结 总结 回溯总结&#xff1a;代码随想录 回溯是递归的副产品&#xff0c;只要有递归就会有回溯&#xff0c;所以回溯法也经常和二叉树遍历&#xff0c;深度优先搜索混在一起&#xff0c;因为这两种方式都是用了递归。 …

《WebKit 技术内幕》学习之十三(1):移动WebKit

1 触控和手势事件 1.1 HTML5规范 随着电容屏幕的流行&#xff0c;触控操作变得前所未有的流行起来。时至今日&#xff0c;带有多点触控功能已经成为了移动设备的标准配置&#xff0c;基于触控的手势识别技术也获得巨大的发展&#xff0c;如使用两个手指来缩放应用的大小等。…

DAY11_(简易版)VUEElement综合案例

目录 1 VUE1.1 概述1.1.1 Vue js文件下载 1.2 快速入门1.3 Vue 指令1.3.1 v-bind & v-model 指令1.3.2 v-on 指令1.3.3 条件判断指令1.3.4 v-for 指令 1.4 生命周期1.5 案例1.5.1 需求1.5.2 查询所有功能1.5.3 添加功能 2 Element2.0 element-ui js和css和字体图标下载2.1 …

Vulnhub靶场DC-6

攻击机192.168.223.128 靶机192.168.223.134 主机发现:nmap -sP 192.168.223.0/24 端口扫描 nmap -sV -p- -A 192.168.223.134 开启了22 80端口&#xff0c;80是apache 2.4.25 先进入web界面看一下 用ip进不去&#xff0c;应该被重定向到wordy.com vim /etc/hosts 加上 19…

亚信安慧AntDB构建未来数据库典范

亚信安慧AntDB是一款数据库管理系统&#xff0c;它采用全球影响力大、社区繁荣、开放度高、生态增长迅速的PG内核。这款系统具有卓越的性能和稳定性&#xff0c;在全球范围内备受用户青睐。 与此同时&#xff0c;AntDB的社区也是充满活力的&#xff0c;用户可以在社区中交流经…

Vue中使用TypeScript:全面指南和最佳实践

🚀 欢迎来到我的专栏!专注于Vue3的实战总结和开发实践分享,让你轻松驾驭Vue3的奇妙世界! 🌈✨在这里,我将为你呈现最新的Vue3技术趋势,分享独家实用教程,并为你解析开发中的难题。让我们一起深入Vue3的魅力,助力你成为Vue大师! 👨‍💻💡不再徘徊,快来关注…

WebSocket服务端数据推送及心跳机制(Spring Boot + VUE):

文章目录 一、WebSocket简介&#xff1a;二、WebSocket通信原理及机制&#xff1a;三、WebSocket特点和优点&#xff1a;四、WebSocket心跳机制&#xff1a;五、在后端Spring Boot 和前端VUE中如何建立通信&#xff1a;【1】在Spring Boot 中 pom.xml中添加 websocket依赖【2】…

都学Python了,C++难道真的用不着了吗?

都学Python了&#xff0c;C难道真的用不着了吗&#xff1f; 在开始前我分享下我的经历&#xff0c;刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;两年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些【C 】学习方法和资料&#xff0c;让我不断提升自己…

单片机介绍

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

关于axios给后端发送数据的问题

这里需要用的插件&#xff1a;qs.js&#xff0c;是前端给后端发送的数组&#xff0c;需要序列化所以要用到这个插件&#xff0c;这里就提取连接在这里&#xff0c;需要的自提&#xff0c;需要导如进来&#xff0c;别忘记了 链接&#xff1a;https://pan.baidu.com/s/1qyD8v9wfd…

拓展全球市场:静态代理IP成为跨境电商战略的关键工具

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

EIGRP实验

实验大纲 一、基本配置 1.构建网络拓扑结构图 2.路由器基本配置 3.配置PC 4.测试连通性 5.保存配置文件 二、配置EIGRP 1.查看路由表 2.配置EIGRP动态路由 3.查看路由器路由表 4.测试网络连通性 5.查看所有路由器的路由协议 6.保存配置文件 三、配置OSPF 1.配置…

SpringMVC-对静态资源的访问

1.工程中加入静态资源 在webapp下创建static文件夹&#xff0c;此文件夹专门放入静态资源 2.使项目可以处理静态资源的请求 在SpringMVC配置文件中添加以下语句 1.引入命名空间 xmlns:mvc"http://www.springframework.org/schema/mvc" xsi:schemaLocation“http…

Influxdb系列(二)influx Cli工具操作influxdb

一、安装并使用 influx CLI 客户端下载地址&#xff1a;https://docs.influxdata.com/influxdb/v2/tools/influx-cli/ [root192 bin]# tar -xvzf influxdb2-client-2.7.3-linux-amd64.tar.gz [root192 bin]# cp influx /usr/local/bin/二、客户端方式创建用户 相关的官网地址…

HNSW算法

From&#xff1a; HNSW算法(nsmlib/hnswlib)-CSDN博客HNSW算法的基本原理及使用 - 知乎 HNSW是一种广泛使用的ANN图索引结构&#xff0c;包括DiskANN、DF-GAS、SmartSSD等。本文档主要总结HNSW的结构与工作流程&#xff0c;便于后期研究其工作流程在迁移到CSD中存在的I/O问题…

JVM篇----第八篇

系列文章目录 文章目录 系列文章目录前言一、标记清除算法( Mark-Sweep)二、复制算法(copying)三、标记整理算法(Mark-Compact)前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分…

华媒舍:15种媒体发稿推广的创意理念与案例分析

媒体发稿已经成为推广知名品牌、产品与服务关键方式之一。怎样通过媒体发稿提升曝光度和吸引住受众却是一个挑战。下面我们就详细介绍15种创意理念和案例分析&#xff0c;帮助你更好地进行新闻媒体发稿推广。 1.造就日常生活小故事通过展示真实用户故事和感受&#xff0c;读者对…

Java实现APK检测管理系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 开放平台模块2.3 软件档案模块2.4 软件检测模块2.5 软件举报模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 开放平台表3.2.2 软件档案表3.2.3 软件检测表3.2.4 软件举报表 四、系统展示五、核心代…

Redis 线程模型

更多内容&#xff0c;前往个人博客 一、概述 【1】Redis 是基于 Reactor 模式开发的网络事件处理器&#xff1a;这个处理器被称为文件事件处理器&#xff08;file event handler&#xff09;&#xff0c;这个文件事件处理器是单线程的&#xff0c;所以 Redis 才叫做单线程的模型…