讯飞星火大模型V3.0 WebApi使用 文档说明:星火认知大模型Web文档 | 讯飞开放平台文档中心 (xfyun.cn) 
实现效果 
初始化 首先构建一个基础脚手架项目
用到如下依赖
1 2 3 4 5 6 7 8 9 "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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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)const  pinia = createPinia()pinia.use(PiniaPluginPersistedstate) app.use(pinia) app.use(router) app.directive("highlight" ,function (el )   let  blocks = el.querySelectorAll('pre code' );   blocks.forEach((block )=> {     highlight.highlightBlock(block);   }) }) app.mount('#app' ) 
TTSRecorder 新建 utils/TTSRecorder.js
这个文件封装了发送消息并相应消息的核心功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 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  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  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    }      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)       }     })   }      webSocketSend (     var  params = {       "header" : {         "app_id" : this .appId,       },       "parameter" : {         "chat" : {                                 "domain" : "generalv3" ,                      "temperature" : 0.5 ,                      "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  =>   }      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 ) {              this .ttsWS.close()     }   } } 
msgStore 新建 stores/msgStore.js
用于存放历史问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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:marked(msg),         status:2        })     },     aiAddMsg (content,status )       let  runMsg = this .list.find(i =>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)         }       }     }   }, }) 
编写界面代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 <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 >