123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- <template>
- <div class="levitatedSphere" v-show="show">
- <div :class="['ls_hello']" v-show="showIndex == 0" @click="stopOne()" @dblclick="stopTwo()">
- <el-image
- style="width: 110%; height: 110%"
- v-show="aiStatus == 1"
- :src="require('../../../assets/icon/course/aiTalk.svg')"
- fit="fill"
- ></el-image>
- <el-image
- style="width: 110%; height: 110%"
- v-show="aiStatus == 2"
- :src="require('../../../assets/icon/course/aiVanish.svg')"
- fit="fill"
- ></el-image>
- <el-image
- style="width: 110%; height: 110%;transform: scale(1.3,1.3);"
- v-show="aiStatus == 0"
- :src="require('../../../assets/icon/course/aiWait2.svg')"
- fit="fill"
- ></el-image>
- </div>
- <div
- class="ls_text"
- :style="{
- width: userText || aiText || showTextIndex == 2 ? '300px' : '0px'
- }"
- >
- <div class="ls_t_ai" v-if="[0, 2].includes(showTextIndex)">
- <span
- v-if="[0].includes(showTextIndex)"
- v-html="htmlContent(aiText)"
- ></span>
- <span v-if="[2].includes(showTextIndex)">正在组织语言...</span>
- </div>
- <div class="ls_t_user" v-if="[1].includes(showTextIndex)">
- {{ userText }}
- </div>
- </div>
- <!-- 录音转文字 -->
- <iframe
- allow="camera *; microphone *;display-capture;midi;encrypted-media;"
- src="https://beta.cloud.cocorobo.cn/browser/public/index.html"
- ref="iiframe"
- v-show="false"
- ></iframe>
- <!-- 文字转语音-->
- <iframe
- allow="camera *; microphone *;display-capture;midi;encrypted-media;"
- src="https://beta.cloud.cocorobo.cn/browser/public/index1.html"
- ref="iiframe2"
- v-show="false"
- ></iframe>
- <mini-audio
- v-show="false"
- ref="audioRef"
- :loop="false"
- :audio-source="wozaiAudioUrl"
- ></mini-audio>
- </div>
- </template>
- <script>
- import { v4 as uuidv4 } from "uuid";
- import MarkdownIt from "markdown-it";
- export default {
- data() {
- return {
- show: false,
- showIndex: 0,
- aiStatus: 0, //0 :在说话 1 : 接收 2:待命
- aiText: "您好,我是小可,有什么可以帮助您的?",
- userText: "",
- showTextIndex: 0, //0:ai,1:用户, 2:组织语言 3: 无
- timer: null,
- isOpen: false,
- userId: this.$route.query.userid,
- chatLoading: false,
- talkLoading: false,
- source: null,
- talkTextList: [],
- wozaiAudioUrl:"https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2F%E4%B8%AD%E6%96%87%E7%89%88%E6%88%91%E5%9C%A81728961654002.wav",
- wozaiAudioEnglishUrl:"https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2F%E8%8B%B1%E6%96%87%E7%89%88what%27sup1728961667376.wav",
- };
- },
- computed: {
- htmlContent() {
- const md = new MarkdownIt();
- return _md => {
- return md.render(_md);
- };
- }
- },
- methods: {
- stopTwo(){
- this.show = false;
- this.showTextIndex = 3;
- this.aiStatus = 2;
- this.aiText = "";
- this.userText = "";
- this.stopRecord();
- },
- stopOne(){
- if(this.source){
- this.source.close();
- this.source = null;
- }
- if (this.talkLoading) {
- this.stopTalk();
- }
- this.showIndex = 0;
- this.showTextIndex = 0;
- this.aiText = "您好,我是小可,有什么可以帮助您的?";
- this.aiStatus = 0;
- this.chatLoading = false;
- },
- recordStart(_text) {
- var OpenCC = require("opencc-js");
- let converter = OpenCC.Converter({
- from: "hk",
- to: "cn"
- });
- // try {
- // this.$parent.changeRecordType(1);
- // return this.$message.success("已开启语音助手,请说“可可同学”来唤醒")
- if (this.isOpen)
- return this.$message.info("已开启语音助手,无需重复开启");
- let iiframe = this.$refs["iiframe"];
- iiframe.contentWindow.window.document.getElementById(
- "languageOptions"
- ).selectedIndex = 2; //普通话
- iiframe.contentWindow.testdoContinuousPronunciationAssessment();
- //this.talkTextList.push("我在");
- //this.talkText();
- // console.log("打开")
- this.$refs.audioRef.play();
- // return;
- // this.$message.success("已开启语音助手,请说“可可同学”来唤醒");
- this.$parent.changeRecordType(1);
- this.isOpen = true;
- this.aiText = "您好,我是小可,有什么可以帮助您的?";
- this.aiStatus = 0;
- this.showIndex = 0;
- this.showTextIndex = 0;
- this.chatLoading = false;
- this.talkLoading == false;
- this.show = true;
- iiframe.contentWindow.onRecognizedResult = e => {
- let _msg = converter(e.privText);
- // let _msg2 = e.privText;
- // let _msg = _text;
- console.log("👇");
- console.log(_msg);
- // _msg = converter(_msg)
- if (!_msg) return console.log("输出为空");
- // if (_msg.indexOf(converter("可可同学")) != -1 && !this.show) {
- // this.aiText = "您好,我是小可,有什么可以帮助您的?";
- // this.aiStatus = 0;
- // this.showIndex = 0;
- // this.show = true;
- // console.log("已唤醒");
- // return;
- // } else
- if (this.show == true) {
- if (
- _msg.indexOf(converter("可可同学")) != -1 &&
- _msg.indexOf(converter("停止")) != -1
- ) {
- this.stopTalk();
- } else if (
- this.showTextIndex == 2 ||
- this.chatLoading ||
- this.talkLoading
- ) {
- return console.log("组织语言中");
- // }else if(_msg.indexOf('可可同学')!=-1 && _msg.indexOf("停止")!=-1){
- // this.stopTalk();
- } else {
- this.showTextIndex = 1;
- this.aiText = "";
- this.userText += _msg;
- this.aiStatus = 1;
- if (this.timer) {
- clearTimeout(this.timer);
- this.timer = null;
- }
- this.timer = setTimeout(() => {
- if (this.userText.indexOf(converter("关闭语音助手")) != -1) {
- // return setTimeout(()=>{
- this.show = false;
- this.showTextIndex = 3;
- this.aiStatus = 2;
- this.aiText = "";
- this.userText = "";
- this.stopRecord();
- return;
- // },1000)
- }
- this.showTextIndex = 2;
- this.aiText = "";
- let regExp = new RegExp(
- converter("计时") + "(.+)" + converter("分钟")
- );
- if (regExp.test(this.userText)) {
- // setTimeout(() => {
- let _number = this.userText.match(regExp)[1];
- let _time = 0;
- if (!/^\d+$/.test(_number)) {
- _time = this.chineseToNumber(_number) * 60;
- } else {
- _time = parseInt(_numberList[1]) * 60;
- }
- this.$emit("startTime", _time);
- this.aiStatus = 0;
- this.showTextIndex = 0;
- this.aiText =
- "好的,我已为您计时" +
- this.userText.match(regExp)[1] +
- "分钟。";
- this.userText = "";
- this.timer = setTimeout(() => {
- this.showTextIndex = 3;
- this.aiStatus = 2;
- this.aiText = "";
- this.userText = "";
- }, 3000);
- // }, 2000);
- } else {
- let msgList = [];
- msgList.push({
- type:"text",
- text: `请在150字以内回答以下问题:\n${this.userText}`
- })
- this.chatLoading = true;
- const _uuid = uuidv4();
- let params = {
- assistant_id: "f8e1ebb2-2e0d-11ef-8bf4-12e77c4cb76b",
- userId: this.userId,
- message: msgList,
- session_name: _uuid + "-qgt",
- uid: _uuid,
- file_ids: [],
- model: "gpt-4o-2024-08-06",
- };
- this.ajax
- // .post("https://claude3.cocorobo.cn/chat", params)
- // .post("https://gpt4.cocorobo.cn/chat", params)
- .post(
- "https://gpt4.cocorobo.cn/ai_agent_park_chat_new",
- params
- )
- .then(res => {
- if (
- converter(res.data.FunctionResponse.result) ==
- converter("发送成功")
- ) {
- this.userText = "";
- this.showIndex = 0;
- } else {
- // this.$message.warning(res.data.FunctionResponse.result);
- console.log(res.data.FunctionResponse.result);
- this.chatLoading = false;
- this.aiStatus = 0;
- this.showTextIndex = 0;
- this.aiText = "对不起,我无法理解您的问题,请重新提问";
- this.timer = setTimeout(() => {
- this.showTextIndex = 3;
- this.aiStatus = 2;
- this.aiText = "";
- this.userText = "";
- }, 3000);
- }
- })
- .catch(e => {
- console.log(e);
- this.chatLoading = false;
- this.aiStatus = 0;
- this.showTextIndex = 0;
- this.aiText = "对不起,我无法理解您的问题,请重新提问";
- this.timer = setTimeout(() => {
- this.showTextIndex = 3;
- this.aiStatus = 2;
- this.aiText = "";
- this.userText = "";
- }, 3000);
- });
- // 通过流获取ai对话数据
- this.getAtAuContent(_uuid);
- }
- }, 5000);
- }
- } else {
- console.log("不响应");
- }
- };
- // } catch (error) {
- // console.log("ai录音报错👇");
- // console.log(error);
- // setTimeout(() => {
- // this.recordStart();
- // }, 1000);
- // }
- },
- stopRecord() {
- // this.$parent.changeRecordType(0);
- // this.$message.success("已关闭语音助手")
- // return
- let iiframe = this.$refs["iiframe"];
- iiframe.contentWindow.window.document
- .getElementById("scenarioStopButton")
- .click();
- if (this.talkLoading) {
- this.stopTalk();
- }
- // this.stopTalk();
- // 录音借宿
- iiframe.contentWindow.onSessionStopped = (s, e) => {
- this.isOpen = false;
- this.show = false;
- this.showTextIndex = 3;
- this.showIndex = 2;
- this.$parent.changeRecordType(0);
- this.$message.success("已关闭语音助手");
- if (this.talkLoading) {
- this.$refs.iiframe2.contentWindow.closesynthesizer();
- }
- this.userText = "";
- this.aiText = "";
- };
- },
- chineseToNumber(chinese) {
- var OpenCC = require("opencc-js");
- let converter = OpenCC.Converter({
- from: "hk",
- to: "cn"
- });
- chinese = converter(chinese);
- const chineseNumbers = {
- 零: 0,
- 一: 1,
- 二: 2,
- 三: 3,
- 四: 4,
- 五: 5,
- 六: 6,
- 七: 7,
- 八: 8,
- 九: 9,
- 十: 10,
- 百: 100,
- 千: 1000,
- 万: 10000,
- 亿: 100000000
- };
- let result = 0;
- let tempNum = 0; // 用于累积处理
- let sectionNum = 0; // 每个段的值
- for (let i = 0; i < chinese.length; i++) {
- const char = chinese[i];
- const num = chineseNumbers[char];
- if (num === undefined) {
- throw new Error(`Unexpected character: ${char}`);
- }
- if (
- num === 10 ||
- num === 100 ||
- num === 1000 ||
- num === 10000 ||
- num === 100000000
- ) {
- if (tempNum === 0) tempNum = 1; // 如果前面没有数,默认是1
- tempNum *= num;
- if (num === 10000 || num === 100000000) {
- sectionNum += tempNum;
- result += sectionNum;
- tempNum = 0;
- sectionNum = 0;
- }
- } else {
- tempNum += num;
- }
- }
- result += sectionNum + tempNum;
- return result;
- },
- removeMarkdown(text) {
- return text
- .replace(/[#*_~`>+\-]/g, "") // 移除 #、*、_、~、`、>、+、- 符号
- .replace(/!\[.*?\]\(.*?\)/g, "") // 移除图片
- .replace(/\[.*?\]\(.*?\)/g, "") // 移除链接
- .replace(/```[\s\S]*?```/g, "") // 移除代码块(不使用 s 标志)
- .replace(/`[^`]*`/g, "") // 移除行内代码
- .replace(/\d+\./g, "") // 移除有序列表
- .replace(/^\s*[-*+]\s+/gm, "") // 移除无序列表
- .replace(/\s+/g, " ") // 将多个空白字符替换为一个空格
- .trim(); // 去除字符串两端的空白字符
- },
- getAtAuContent(_uid) {
- this.source = new EventSource(
- `https://gpt4.cocorobo.cn/question/${_uid}`
- );
- //http://gpt4.cocorobo.cn:8011/question/ https://gpt4.cocorobo.cn/question/
- let _allText = "";
- let _mdText = "";
- let _talkText = "";
- // let _talkIndex = 0;
- // const md = new MarkdownIt();
- this.source.onmessage = _e => {
- this.showIndex = 0;
- this.aiStatus = 0
- let _eData = JSON.parse(_e.data);
- if (_eData.content.replace("'", "").replace("'", "") == "[DONE]") {
- let _result = [];
- if ("result" in _eData) {
- _result = _eData.result;
- for (let i = 0; i < _result.length; i++) {
- _mdText = _mdText.replace(_result[i].text, _result[i].fileName);
- }
- }
- _mdText = _mdText.replace("_", "");
- this.aiText = _mdText;
- if (_talkText != "") {
- let _resultText = this.removeMarkdown(_talkText);
- this.talkTextList.push(_resultText);
- _talkText = "";
- if (!this.talkLoading) this.talkText();
- }
- this.chatLoading = false;
- this.source.close();
- } else {
- // _talkIndex+=1;
- let _text = _eData.content.replace("'", "").replace("'", "");
- if (_allText == "") {
- _allText = _text.replace(/^\n+/, ""); //去掉回复消息中偶尔开头就存在的连续换行符
- _talkText += _text.replace(/^\n+/, "");
- } else {
- _allText += _text;
- _talkText += _text;
- }
- `~`;
- _mdText = _allText + "_";
- _mdText = _mdText.replace(/\\n/g, "\n");
- _mdText = _mdText.replace(/\\/g, "");
- if (_allText.split("```").length % 2 == 0) _mdText += "\n```\n";
- this.aiText = _mdText;
- this.showTextIndex = 0;
- if (/[,。:;?!)]/.test(_talkText)) {
- let _resultText = this.removeMarkdown(_talkText);
- this.talkTextList.push(_resultText);
- _talkText = "";
- if (!this.talkLoading) this.talkText();
- }
- }
- };
- },
- talkText() {
- let _text = this.talkTextList.shift();
- let _talkTextIiframe2 = this.$refs.iiframe2;
- if (_text) {
- this.talkLoading = true;
- if (this.timer) {
- clearTimeout(this.timer);
- this.timer = null;
- }
- console.log(`👉转语音:${_text}`);
- _talkTextIiframe2.contentWindow.texttospeech(
- _text,
- this.talkText,
- this.endTalk
- );
- } else {
- _talkTextIiframe2.contentWindow.closesynthesizer();
- }
- },
- endTalk() {
- console.log("👉转语音结束👈");
- this.talkLoading = false;
- },
- stopTalk() {
- this.talkTextList = [];
- let _talkTextIiframe2 = this.$refs.iiframe2;
- _talkTextIiframe2.contentWindow.closesynthesizer();
- _talkTextIiframe2.contentWindow.pausesynthesizer();
- this.talkLoading = false;
- }
- },
- mounted() {
- // // this.recordStart()
- // setTimeout(()=>{
- // this.recordStart("可可同学。")
- // setTimeout(()=>{
- // this.recordStart("世界上最大的火山是什么。")
- // setTimeout(()=>{
- // this.recordStart("位于哪里。他温度怎么样。")
- // },2000)
- // },2000)
- // },5000)
- }
- };
- </script>
- <style scoped>
- .levitatedSphere {
- position: fixed;
- width: auto;
- height: auto;
- top: 20px;
- right: 10px;
- z-index: 9999;
- display: flex;
- }
- .ls_hello {
- width: 80px;
- height: 80px;
- position: absolute;
- top: 0;
- right: 0;
- cursor: pointer;
- animation: smallToBig 1s both;
- }
- /* 定义渐入动画 */
- @keyframes smallToBig {
- from {
- opacity: 0;
- transform: scale(0.1);
- }
- to {
- opacity: 1;
- transform: scale(1);
- }
- }
- .ls_text {
- position: absolute;
- right: 100px;
- top: 20px;
- max-width: 300px;
- display: flex;
- justify-content: flex-end;
- }
- .ls_t_ai {
- padding: 12px;
- width: auto;
- height: auto;
- border-radius: 12px 0px 12px 12px;
- background: #00000099;
- color: #fff;
- }
- .ls_t_user {
- padding: 12px;
- width: auto;
- height: auto;
- border-radius: 12px 12px 12px 12px;
- background: #00000099;
- color: #fff;
- }
- </style>
|