一文带你搞懂Vue3如何使用讯飞大模型

 更新时间:2024年12月12日 08:38:12   作者:逆旅行天涯  
这篇文章主要为大家详细介绍了Vue3使用讯飞大模型的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

初始化

首先构建一个基础脚手架项目

用到如下依赖

"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并设置持久化缓存
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

这个文件封装了发送消息并相应消息的核心功能

import CryptoJS from "crypto-js"
const APPID = '' // 从控制台可以获取
const API_SECRET = '' // 从控制台可以获取
const API_KEY = '' // 从控制台可以获取
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报错,请f12查看详情')
        console.error(`详情查看:${encodeURI(url.replace('wss:', 'https:'))}`)
      }
      ttsWS.onclose = e => {
        console.log(e)
      }
    })
  }
 
 
  // websocket发送数据
  webSocketSend() {
    var params = {
      "header": {
        "app_id": this.appId,
      },
      "parameter": {
        "chat": {
          // 指定访问的领域,general指向V1.5版本,generalv2指向V2版本,generalv3指向V3版本 。
          // 注意:不同的取值对应的url也不一样!
          // "domain": "generalv3",
          "domain": "lite",
          // 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高
          "temperature": 0.5,
          // 模型回答的tokens的最大长度
          "max_tokens": 1024
        }
      },
      "payload": {
        "message": {
          "text": this.msgStore.list
        }
      }
    }
    console.log(params,'请求的参数')
    this.ttsWS.send(JSON.stringify(params))
  }
 
  start(msgStore,msgDom) {
    this.msgStore = msgStore
    this.msgDom = msgDom.value
    total_res = ""; // 请空回答历史
    this.connectWebSocket().then(r => {})
  }
 
  // websocket接收数据的处理
  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) {
      // 关闭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="请输入问题" class="text" v-model="msgValue"></textarea>
      <button class="btn" @click="submitMsg">发送</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()
})
 
// 滚动到最底部
const scroll = () => {
  nextTick(()=>{
    msgDom.value.scrollTop = msgDom.value.scrollHeight
  })
}
 
// 发送消息
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;
}

到此这篇关于一文带你搞懂Vue3如何使用讯飞大模型的文章就介绍到这了,更多相关Vue3使用讯飞大模型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue3 动态绑定背景图片方法示例

    vue3 动态绑定背景图片方法示例

    这篇文章主要为大家介绍了vue3动态绑定背景图片实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • vue项目兼容ie11的实现方法

    vue项目兼容ie11的实现方法

    本文主要介绍了vue项目兼容ie11的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • 关于vue 的slot分发内容 (多个分发)

    关于vue 的slot分发内容 (多个分发)

    这篇文章主要介绍了关于vue 的slot分发内容 (多个分发),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • vue使用pdfjs显示PDF可复制的实现方法

    vue使用pdfjs显示PDF可复制的实现方法

    这篇文章主要介绍了vue使用pdfjs显示PDF可复制的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • petite vue的使用示例详解

    petite vue的使用示例详解

    这篇文章主要为大家介绍了petite vue的使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • vue2从数据变化到视图变化之nextTick使用详解

    vue2从数据变化到视图变化之nextTick使用详解

    这篇文章主要为大家介绍了vue2从数据变化到视图变化之nextTick使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • vue监听路由变化时watch方法会执行多次的原因及解决

    vue监听路由变化时watch方法会执行多次的原因及解决

    这篇文章主要介绍了vue监听路由变化时watch方法会执行多次的原因及解决,帮助大家更好的理解和学习使用vue框架,感兴趣的朋友可以了解下
    2021-04-04
  • Vue自定义询问弹框和输入弹框的示例代码

    Vue自定义询问弹框和输入弹框的示例代码

    这篇文章主要介绍了Vue自定义询问弹框和输入弹框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • vuex页面刷新后数据丢失的方法

    vuex页面刷新后数据丢失的方法

    这篇文章主要介绍了vuex页面刷新后数据丢失的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • Vue实现实时监听页面宽度高度变化

    Vue实现实时监听页面宽度高度变化

    这篇文章主要为大家详细介绍了Vue如何实现实时监听页面宽度高度变化,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-03-03

最新评论