一文帶你搞懂Vue3如何使用訊飛大模型
初始化
首先構(gòu)建一個(gè)基礎(chǔ)腳手架項(xiàng)目
用到如下依賴
"dependencies": { "crypto-js": "^4.2.0", "highlight.js": "^11.9.0", "marked": "^9.1.3", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.0", "vue": "^3.3.4", "vue-router": "^4.2.5" }
修改 main.js
import './assets/main.css' import { createApp } from 'vue' import { createPinia } from 'pinia' import PiniaPluginPersistedstate from "pinia-plugin-persistedstate" import App from './App.vue' import router from './router' import highlight from 'highlight.js' import "highlight.js/styles/atom-one-dark.css" const app = createApp(App) // 配置Pinia并設(shè)置持久化緩存 const pinia = createPinia() pinia.use(PiniaPluginPersistedstate) app.use(pinia) app.use(router) // 配置Markdown語法高亮 app.directive("highlight",function(el){ let blocks = el.querySelectorAll('pre code'); blocks.forEach((block)=>{ highlight.highlightBlock(block); }) }) app.mount('#app')
TTSRecorder
新建 utils/TTSRecorder.js
這個(gè)文件封裝了發(fā)送消息并相應(yīng)消息的核心功能
import CryptoJS from "crypto-js" const APPID = '' // 從控制臺(tái)可以獲取 const API_SECRET = '' // 從控制臺(tái)可以獲取 const API_KEY = '' // 從控制臺(tái)可以獲取 let total_res = ""; function getWebsocketUrl() { return new Promise((resolve, reject) => { var apiKey = API_KEY var apiSecret = API_SECRET // var url = 'ws://spark-api.xf-yun.com/v3.1/chat' var url = 'ws://spark-api.xf-yun.com/v1.1/chat' var host = location.host var date = new Date().toGMTString() var algorithm = 'hmac-sha256' var headers = 'host date request-line' // var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v3.1/chat HTTP/1.1` var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v1.1/chat HTTP/1.1` var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret) var signature = CryptoJS.enc.Base64.stringify(signatureSha) var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"` var authorization = btoa(authorizationOrigin) url = `${url}?authorization=${authorization}&date=${date}&host=${host}` resolve(url) }) } export default class TTSRecorder { constructor({appId = APPID} = {}) { this.appId = appId this.msgStore = null this.msgDom = null } // 連接websocket connectWebSocket() { return getWebsocketUrl().then(url => { let ttsWS if ('WebSocket' in window) { ttsWS = new WebSocket(url) } else if ('MozWebSocket' in window) { ttsWS = new MozWebSocket(url) } else { alert('瀏覽器不支持WebSocket') return } this.ttsWS = ttsWS ttsWS.onopen = e => { this.webSocketSend() } ttsWS.onmessage = e => { this.result(e.data) } ttsWS.onerror = e => { alert('WebSocket報(bào)錯(cuò),請(qǐng)f12查看詳情') console.error(`詳情查看:${encodeURI(url.replace('wss:', 'https:'))}`) } ttsWS.onclose = e => { console.log(e) } }) } // websocket發(fā)送數(shù)據(jù) webSocketSend() { var params = { "header": { "app_id": this.appId, }, "parameter": { "chat": { // 指定訪問的領(lǐng)域,general指向V1.5版本,generalv2指向V2版本,generalv3指向V3版本 。 // 注意:不同的取值對(duì)應(yīng)的url也不一樣! // "domain": "generalv3", "domain": "lite", // 核采樣閾值。用于決定結(jié)果隨機(jī)性,取值越高隨機(jī)性越強(qiáng)即相同的問題得到的不同答案的可能性越高 "temperature": 0.5, // 模型回答的tokens的最大長(zhǎng)度 "max_tokens": 1024 } }, "payload": { "message": { "text": this.msgStore.list } } } console.log(params,'請(qǐng)求的參數(shù)') this.ttsWS.send(JSON.stringify(params)) } start(msgStore,msgDom) { this.msgStore = msgStore this.msgDom = msgDom.value total_res = ""; // 請(qǐng)空回答歷史 this.connectWebSocket().then(r => {}) } // websocket接收數(shù)據(jù)的處理 result(resultData) { let jsonData = JSON.parse(resultData) jsonData.payload.choices.text.forEach(res=>{ this.msgStore.aiAddMsg(res.content,jsonData.header.status) this.msgDom.scrollTop = this.msgDom.scrollHeight + 500 }) // 提問失敗 if (jsonData.header.code !== 0) { alert(`提問失敗: ${jsonData.header.code}:${jsonData.header.message}`) console.error(`${jsonData.header.code}:${jsonData.header.message}`) return } if (jsonData.header.code === 0 && jsonData.header.status === 2) { // 關(guān)閉WebSocket this.ttsWS.close() } } }
msgStore
新建 stores/msgStore.js
用于存放歷史問題
import { defineStore } from 'pinia' import { marked } from 'marked' export const userMsgStore = defineStore("userMsgStore",{ // 持久化 persist: true, state: () => { return { list:[] } }, actions: { userAddMsg(msg) { this.list.push({ role:"user", content:msg, status:2 }) }, aiAddMsg(content,status){ let runMsg = this.list.find(i=>i.status !== 2) if(!runMsg){ this.list.push({ role:"assistant", content:content, status:status }) }else{ runMsg.content += content runMsg.status = status if(status === 2){ runMsg.content = marked(runMsg.content) } } } }, })
編寫界面代碼
<template> <div class="content"> <div class="message" id='message-box'> <div v-for="(msg,index) in msgList" :key="index" :class="{ 'user':msg.role === 'user', 'assistant':msg.role === 'assistant' }"> <div> <div> <img class='role-img' :src="userImg" v-if="msg.role === 'user'"/> </div> <div class='imgbox' v-if="msg.role === 'assistant'"> <img class='role-img' :src="aiImg" /> <div class='name'>訊飛AI</div> </div> </div> <div v-highlight v-html='msg.content'></div> </div> </div> <div class="footer"> <textarea rows="5" placeholder="請(qǐng)輸入問題" class="text" v-model="msgValue"></textarea> <button class="btn" @click="submitMsg">發(fā)送</button> </div> </div> </template> <script setup> import userImg from "@/assets/user.png" import aiImg from "@/assets/ai.png" import { nextTick, onMounted, ref } from 'vue' import TTSRecorder from "@/utils/TTSRecorder" import { userMsgStore } from '@/stores/msgStore' const msgStore = userMsgStore() const msgValue = ref("") let ttsRecorder = new TTSRecorder() const msgList = ref([]) let msgDom = ref(null) onMounted(()=>{ msgDom.value = document.getElementById("message-box") msgList.value = msgStore.list scroll() }) // 滾動(dòng)到最底部 const scroll = () => { nextTick(()=>{ msgDom.value.scrollTop = msgDom.value.scrollHeight }) } // 發(fā)送消息 const submitMsg = async () => { msgStore.userAddMsg(msgValue.value) msgValue.value = "" // 開始提問 ttsRecorder.start(msgStore,msgDom) scroll() } </script> <style scoped lang="less"> .content{ height: 100%; position: relative; .message{ position: absolute; top: 0; left: 20%; right: 20%; bottom: 150px; display: flex; overflow: auto; flex-direction: column; .user{ background-color: #ebf7f8; padding: 15px; box-sizing: border-box; display: flex; flex-direction: column; align-items: flex-end; border-bottom: 1px solid #dfdfdf; } .assistant{ background-color: #f7f7f7; padding: 15px; box-sizing: border-box; border-bottom: 1px solid #dfdfdf; } } .footer{ position: absolute; bottom: 50px; left: 20%; right: 20%; display: flex; align-items: flex-end; gap: 15px; .text{ width: 100%; } .btn{ width: 100px; height: 40px; background-color: #1a60ea; color: white; border: none; } } @media screen and (max-width: 768px) { .message,.footer { left: 0; right: 0; } .message{ bottom: 100px; } .footer{ bottom: 10px; } } } .imgbox{ display: flex; align-items: center; gap: 10px; margin-bottom: 10px; .name{ font-size: 13px; color: #fd919e; font-weight: 400; } } .role-img{ width: 40px; height: 40px; border-radius: 50%; overflow: hidden; } </style>
main.css 修改
@import './base.css'; #app { height: 100vh; overflow: auto; }
到此這篇關(guān)于一文帶你搞懂Vue3如何使用訊飛大模型的文章就介紹到這了,更多相關(guān)Vue3使用訊飛大模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Vue CLI3 多頁應(yīng)用實(shí)踐和源碼設(shè)計(jì)
這篇文章主要介紹了詳解Vue CLI3 多頁應(yīng)用實(shí)踐和源碼設(shè)計(jì),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08Vue引用vee-validate插件表單驗(yàn)證問題(cdn方式引用)
這篇文章主要介紹了Vue引用vee-validate插件表單驗(yàn)證問題(cdn方式引用),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12Vue3+Vite項(xiàng)目使用less的實(shí)現(xiàn)步驟
最近學(xué)習(xí)在vite項(xiàng)目中配置less,本文主要介紹了Vue3+Vite項(xiàng)目使用less的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02Element 默認(rèn)勾選表格 toggleRowSelection的實(shí)現(xiàn)
這篇文章主要介紹了Element 默認(rèn)勾選表格 toggleRowSelection的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09vue2使用wangeditor實(shí)現(xiàn)手寫輸入功能
這篇文章主要為大家詳細(xì)介紹了vue2如何使用wangeditor實(shí)現(xiàn)手寫輸入功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下2023-12-12