Commit f82a7aff authored by 邹绪超's avatar 邹绪超

feat: 完成UI界面

parent 2bf6c416
Pipeline #85817 waiting for manual action with stages
...@@ -64,9 +64,11 @@ const PandaUi = props => { ...@@ -64,9 +64,11 @@ const PandaUi = props => {
events : { events : {
frame (data) { frame (data) {
event.emit("aiSound:frame", data) event.emit("aiSound:frame", data)
console.log(data.resultTextTemp)
}, },
finish (data) { finish (data) {
event.emit("aiSound:finish", data) event.emit("aiSound:finish", data)
console.log(data.resultText)
}, },
fail ({data}) { fail ({data}) {
message.info(data.msg) message.info(data.msg)
...@@ -75,6 +77,7 @@ const PandaUi = props => { ...@@ -75,6 +77,7 @@ const PandaUi = props => {
loading: false, loading: false,
checked: false checked: false
}) })
setIsStartRecorder(false)
} }
} }
}) })
......
import style from './style.less' import style from './style.less'
import { QuestionCircleOutlined } from '@ant-design/icons'
import { withRouter } from '@wisdom-utils/runtime' import { withRouter } from '@wisdom-utils/runtime'
import { Tooltip, Typography } from 'antd' import { Tooltip, Typography } from 'antd'
import { Fragment, useCallback, useEffect, useRef, useState } from 'react' import { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { event } from '@wisdom-utils/utils' import { event } from '@wisdom-utils/utils'
import moment from 'moment' import moment from 'moment'
import { Directive } from '../../core' import { Directive , AllDirectiveTypes} from '../../core'
import config from '../../config'
const Dialog = props => { const Dialog = props => {
const { pandaRecordWebSocketRef } = props const { pandaRecordWebSocketRef , close} = props
const ulRef = useRef(null) const ulRef = useRef(null)
const directiveRef = useRef(null) const directiveRef = useRef(null)
const toolTipRef = useRef(null)
const timeRef = useRef(null)
const [data, setData] = useState([]) const [data, setData] = useState([])
useEffect(() => {
if (!ulRef.current.lastChild) return
ulRef.current.lastChild.scrollIntoView({
block: 'end',
behavior: 'smooth'
})
startTime()
}, [data])
useEffect(() => {
directiveRef.current = new Directive({
params: {
}
})
event.on("aiSound:finish", finish)
event.on("aiSound:frame", frame)
startTime()
return () => {
event.off("aiSound:finish", finish)
event.off("aiSound:frame", frame)
destroyTime()
}
}, [])
const finish = useCallback(async ({ resultText }) => { const finish = useCallback(async ({ resultText }) => {
pandaRecordWebSocketRef.current.setStatus("pause") pandaRecordWebSocketRef.current.setStatus("pause")
...@@ -38,14 +65,6 @@ const Dialog = props => { ...@@ -38,14 +65,6 @@ const Dialog = props => {
pandaRecordWebSocketRef.current.setStatus("playing") pandaRecordWebSocketRef.current.setStatus("playing")
}, []) }, [])
useEffect(() => {
if (!ulRef.current.lastChild) return
ulRef.current.lastChild.scrollIntoView({
block: 'end',
behavior: 'smooth'
})
}, [data])
const frame = useCallback((data) => { const frame = useCallback((data) => {
const { resultText, resultTextTemp } = data const { resultText, resultTextTemp } = data
console.log(resultTextTemp) console.log(resultTextTemp)
...@@ -76,22 +95,6 @@ const Dialog = props => { ...@@ -76,22 +95,6 @@ const Dialog = props => {
}) })
}, []) }, [])
useEffect(() => {
directiveRef.current = new Directive({
params: {
}
})
}, [])
useEffect(() => {
event.on("aiSound:finish", finish)
event.on("aiSound:frame", frame)
return () => {
event.off("aiSound:finish", finish)
event.off("aiSound:frame", frame)
}
}, [])
const sendContent = ({ role = "system", content, ...params }) => { const sendContent = ({ role = "system", content, ...params }) => {
setData(i => i.concat([{ setData(i => i.concat([{
role, role,
...@@ -117,6 +120,19 @@ const Dialog = props => { ...@@ -117,6 +120,19 @@ const Dialog = props => {
})) }))
} }
const startTime = () => {
destroyTime()
timeRef.current = setTimeout(() => {
close && close()
}, config.closePandaAiTime)
}
const destroyTime = () => {
if(timeRef.current) {
clearTimeout(timeRef.current)
timeRef.current = null
}
}
return (<div className={style.container}> return (<div className={style.container}>
<div className={style.pandaIcon}></div> <div className={style.pandaIcon}></div>
...@@ -126,19 +142,23 @@ const Dialog = props => { ...@@ -126,19 +142,23 @@ const Dialog = props => {
<div className={style.help}> <div className={style.help}>
<Tooltip <Tooltip
placement="bottomRight" placement="bottomRight"
title = {<span>邹绪超</span>} title = {
visible = {true} <div className={style.toolTipContent}>
<p className={style.toolTipTitle}>支持以下语音指令集</p>
<div className={style.toolTipWrapper}>
<ul>
{
AllDirectiveTypes.map((item , index) => <li key={index}>{index + 1}.{item.name}</li>)
}
</ul>
</div>
</div>
}
getPopupContainer = {() => toolTipRef.current}
arrowPointAtCenter = {true}
> >
<span className={style.helpIcon}></span> <span className={style.helpIcon} ref = {toolTipRef}></span>
</Tooltip> </Tooltip>
{/* <div className={style.toolTipContent}>
<p>支持以下语音指令集</p>
<ul className={style.toolTip}>
{
[].map((name, index) => <li key={index}>{index + 1}.{name}</li>)
}
</ul>
</div> */}
</div> </div>
</div> </div>
<div className={style.content}> <div className={style.content}>
......
...@@ -73,34 +73,27 @@ ...@@ -73,34 +73,27 @@
} }
.toolTipContent { .toolTipContent {
display: none; display: flex;
position: absolute; flex-direction: column;
white-space: nowrap; .toolTipTitle{
right: 0; margin-bottom: 5px;
font-weight: normal; white-space: nowrap;
font-size: 14px;
// background: #f5f5f5;
padding: 0 10px;
border-radius: 5px;
z-index: 10;
>p {
margin-bottom: 0;
font-weight: bold;
} }
.toolTipWrapper{
.toolTip { max-height: 200px;
display: flex; overflow-y: auto;
flex-direction: column; >ul{
margin-bottom: 0; list-style: none;
flex-direction: column;
>li {
white-space: nowrap;
display: flex; display: flex;
align-items: center; max-width: 207px;
height: 30px; >li{
margin-bottom: 5px;
width: 100%;
}
} }
} }
} }
} }
......
...@@ -4,12 +4,16 @@ import image from '@/assets/images/soundAi/眨眼.gif' ...@@ -4,12 +4,16 @@ import image from '@/assets/images/soundAi/眨眼.gif'
import { event } from '@wisdom-utils/utils' import { event } from '@wisdom-utils/utils'
import { AwakenDirective, ByeDirective, Directive } from '../../core' import { AwakenDirective, ByeDirective, Directive } from '../../core'
import Dialog from '../Dialog' import Dialog from '../Dialog'
import config from '../../config'
const PandaTip = props => { const PandaTip = props => {
const { pandaRecordWebSocketRef } = props
const [status, setStatus] = useState("close") const [status, setStatus] = useState("close")
const directiveRef = useRef(null) const directiveRef = useRef(null)
const { pandaRecordWebSocketRef } = props const [initOpen, setInitOpen] = useState(false)
const [showTip, setShowTip] = useState(true)
const timeRef = useRef(null)
const finish = useCallback(({ resultText }) => { const finish = useCallback(({ resultText }) => {
const directive = directiveRef.current.parse({ const directive = directiveRef.current.parse({
text: resultText text: resultText
...@@ -30,17 +34,54 @@ const PandaTip = props => { ...@@ -30,17 +34,54 @@ const PandaTip = props => {
useEffect(() => { useEffect(() => {
event.on("aiSound:finish", finish) event.on("aiSound:finish", finish)
if (status == "open") {
destroyTime()
} else if (status == "close") {
if (initOpen == true) {
startTime()
setShowTip(false)
} else {
setShowTip(true)
}
}
return () => { return () => {
event.off("aiSound:finish", finish) event.off("aiSound:finish", finish)
} }
}, [status]) }, [status , initOpen])
const startTime = () => {
destroyTime()
timeRef.current = setTimeout(() => {
setShowTip(true)
}, config.showOpenAiTipTime)
}
const destroyTime = () => {
if (timeRef.current) {
clearTimeout(timeRef.current)
timeRef.current = null
}
}
const close = () => {
setStatus("close")
setInitOpen(true)
// let count = 0
// setInterval(() => {
// ++count;
// console.log(count)
// }, 1000)
}
return (<div className={style.container}> return (<div className={style.container}>
{ {
status == "open" ? <Dialog pandaRecordWebSocketRef={pandaRecordWebSocketRef} /> : <div className={style.content}> status == "open" ? <Dialog close={close} pandaRecordWebSocketRef={pandaRecordWebSocketRef} /> : <div className={style.content}>
<div className={style.tipImage}> {
<span>呼叫“熊猫熊猫”唤醒语音小助手。</span> showTip ? <div className={style.tipImage}>
</div> <span>呼叫“熊猫熊猫”唤醒语音小助手。</span>
</div> : null
}
<img src={image} className={style.image} /> <img src={image} className={style.image} />
</div> </div>
} }
......
const config = {
closePandaAiTime : 1000 * 60,
showOpenAiTipTime : 1000 * 60
}
export default config
\ No newline at end of file
...@@ -23,6 +23,7 @@ class PandaRecordWebSocket extends PandaWebSocket { ...@@ -23,6 +23,7 @@ class PandaRecordWebSocket extends PandaWebSocket {
this.count = 0; this.count = 0;
this.initFrameBuffer = null; this.initFrameBuffer = null;
this.status = "playing"; this.status = "playing";
this.startTime = new Date()
} }
async start() { async start() {
...@@ -132,13 +133,14 @@ class PandaRecordWebSocket extends PandaWebSocket { ...@@ -132,13 +133,14 @@ class PandaRecordWebSocket extends PandaWebSocket {
return isEmpty; return isEmpty;
} }
onRecorderFrameRecorded({ isLastFrame, frameBuffer }) { onRecorderFrameRecorded({ isLastFrame }) {
const frameBuffer = new ArrayBuffer(1280)
const { encoding, format } = this.config; const { encoding, format } = this.config;
if (!this.ws) { if (!this.ws) {
const isEmptyData = this.getIsEmptyData(frameBuffer, 200, 20); const isEmptyData = this.getIsEmptyData(frameBuffer, 200, 20);
if (isEmptyData) { if (isEmptyData) {
this.initFrameBuffer = null; this.initFrameBuffer = null;
console.log('系统檢測到輸入'); console.log('系统未检测到输入');
return; return;
} }
if(this.status == "paused") return if(this.status == "paused") return
...@@ -148,6 +150,32 @@ class PandaRecordWebSocket extends PandaWebSocket { ...@@ -148,6 +150,32 @@ class PandaRecordWebSocket extends PandaWebSocket {
this.createWs(); this.createWs();
} }
if (this.ws.readyState === this.ws.OPEN) { if (this.ws.readyState === this.ws.OPEN) {
// if((new Date().getTime() - this.startTime.getTime()) >= (this.config.vad_eos )) {
// console.log("主动断开")
// this.ws.send(
// JSON.stringify({
// data: {
// format,
// encoding,
// status: 2,
// },
// }),
// );
// this.wsClose()
// } else {
// console.log("2222")
// this.ws.send(
// JSON.stringify({
// data: {
// format,
// encoding,
// status: isLastFrame ? 2 : 1,
// audio: Utils.toBase64(frameBuffer),
// },
// }),
// );
// }
this.ws.send( this.ws.send(
JSON.stringify({ JSON.stringify({
data: { data: {
...@@ -162,10 +190,24 @@ class PandaRecordWebSocket extends PandaWebSocket { ...@@ -162,10 +190,24 @@ class PandaRecordWebSocket extends PandaWebSocket {
this.destroy(); this.destroy();
} }
} }
} }
reconnect() { reconnect() {
const { encoding, format } = this.config;
this.count += 1; this.count += 1;
if (this.ws && this.ws.readyState === this.ws.OPEN) {
this.ws.send(
JSON.stringify({
data: {
format,
encoding,
status: 2,
},
}),
);
}
this.wsClose(); this.wsClose();
} }
...@@ -202,9 +244,11 @@ class PandaRecordWebSocket extends PandaWebSocket { ...@@ -202,9 +244,11 @@ class PandaRecordWebSocket extends PandaWebSocket {
data, data,
}; };
this.ws.send(JSON.stringify(params)); this.ws.send(JSON.stringify(params));
this.startTime = new Date()
} }
wsOnmessage(e) { wsOnmessage(e) {
this.startTime = new Date()
super.wsOnmessage(e); super.wsOnmessage(e);
const jsonData = JSON.parse(e.data); const jsonData = JSON.parse(e.data);
if (jsonData.data && jsonData.data.result) { if (jsonData.data && jsonData.data.result) {
...@@ -216,7 +260,7 @@ class PandaRecordWebSocket extends PandaWebSocket { ...@@ -216,7 +260,7 @@ class PandaRecordWebSocket extends PandaWebSocket {
} }
// 开启wpgs会有此字段(前提:在控制台开通动态修正功能) // 开启wpgs会有此字段(前提:在控制台开通动态修正功能)
// 取值为 "apd"时表示该片结果是追加到前面的最终结果;取值为"rpl" 时表示替换前面的部分结果,替换范围为rg字段 // 取值为 "apd"时表示该片结果是追加到前面的最终结果;取值为"rpl" 时表示替换前面的部分结果,替换范围为rg字段
if (data.pgs) { if (data.pgs) {
if (data.pgs === 'apd') { if (data.pgs === 'apd') {
// 将resultTextTemp同步给resultText // 将resultTextTemp同步给resultText
...@@ -227,15 +271,14 @@ class PandaRecordWebSocket extends PandaWebSocket { ...@@ -227,15 +271,14 @@ class PandaRecordWebSocket extends PandaWebSocket {
} else { } else {
this.resultText = this.resultText + str; this.resultText = this.resultText + str;
} }
if(!this.resultTextTemp) { if(this.resultTextTemp) {
return this.events.frame({
resultTextTemp: this.resultTextTemp,
resultText: this.resultText,
pgs: data.pgs,
count: this.count,
});
} }
this.events.frame({
resultTextTemp: this.resultTextTemp,
resultText: this.resultText,
pgs: data.pgs,
count: this.count,
});
} }
if (jsonData.code == 0 && jsonData.data.status == 2) { if (jsonData.code == 0 && jsonData.data.status == 2) {
const resultText = this.resultTextTemp ? this.resultTextTemp : this.resultText const resultText = this.resultTextTemp ? this.resultTextTemp : this.resultText
......
import BaseDirective from "./BaseDirective" import BaseDirective from "./BaseDirective"
class AwakenDirective extends BaseDirective{ class AwakenDirective extends BaseDirective{
static name = "熊猫熊猫唤醒"
match () { match () {
const {text} = this const {text} = this
if(/熊猫熊猫/.test(text)) { if(/熊猫熊猫/.test(text)) {
......
import BaseDirective from "./BaseDirective"; import BaseDirective from "./BaseDirective";
class ByeDirective extends BaseDirective { class ByeDirective extends BaseDirective {
static name = "再见或者拜拜关闭熊猫"
match () { match () {
const {text} = this const {text} = this
if(/再见|拜拜|关闭熊猫/.test(text)) { if(/再见|拜拜|关闭熊猫/.test(text)) {
......
...@@ -3,6 +3,7 @@ import {store , event} from '@wisdom-utils/utils' ...@@ -3,6 +3,7 @@ import {store , event} from '@wisdom-utils/utils'
import Utils from "../utils/Utils"; import Utils from "../utils/Utils";
class CloseMenuDirective extends BaseDirective { class CloseMenuDirective extends BaseDirective {
static name="关闭菜单或者模糊匹配关闭菜单"
match () { match () {
const { text } = this const { text } = this
let result = null let result = null
......
...@@ -2,6 +2,7 @@ import Utils from '../utils/Utils' ...@@ -2,6 +2,7 @@ import Utils from '../utils/Utils'
import BaseDirective from './BaseDirective' import BaseDirective from './BaseDirective'
class HomePageDirective extends BaseDirective { class HomePageDirective extends BaseDirective {
static name = "返回首页"
match () { match () {
const {text} = this const {text} = this
if(/首页/.test(text)) { if(/首页/.test(text)) {
......
...@@ -4,6 +4,7 @@ import Utils from "../utils/Utils"; ...@@ -4,6 +4,7 @@ import Utils from "../utils/Utils";
import { uniq } from "lodash"; import { uniq } from "lodash";
class OpenMenuDirective extends BaseDirective { class OpenMenuDirective extends BaseDirective {
static name="打开菜单或者模糊匹配打开菜单"
match() { match() {
const { text } = this const { text } = this
let result = null let result = null
......
...@@ -2,6 +2,7 @@ import AwakenDirective from "./AwakenDirective" ...@@ -2,6 +2,7 @@ import AwakenDirective from "./AwakenDirective"
import OpenMenuDirective from "./OpenMenuDirective" import OpenMenuDirective from "./OpenMenuDirective"
import HomePageDirective from "./HomePageDirective" import HomePageDirective from "./HomePageDirective"
import CloseMenuDirective from "./CloseMenuDirective" import CloseMenuDirective from "./CloseMenuDirective"
import ByeDirective from "./ByeDirective"
const DirectiveTypes = [ const DirectiveTypes = [
AwakenDirective, AwakenDirective,
...@@ -9,6 +10,13 @@ const DirectiveTypes = [ ...@@ -9,6 +10,13 @@ const DirectiveTypes = [
OpenMenuDirective, OpenMenuDirective,
CloseMenuDirective CloseMenuDirective
] ]
const AllDirectiveTypes = [
AwakenDirective,
HomePageDirective,
OpenMenuDirective,
CloseMenuDirective,
ByeDirective
]
class Directive { class Directive {
constructor (options = {}) { constructor (options = {}) {
this.directiveTypes = options.directiveTypes || DirectiveTypes this.directiveTypes = options.directiveTypes || DirectiveTypes
...@@ -40,4 +48,8 @@ class Directive { ...@@ -40,4 +48,8 @@ class Directive {
} }
} }
export default Directive export default Directive
\ No newline at end of file export {
DirectiveTypes,
AllDirectiveTypes
}
\ No newline at end of file
...@@ -3,7 +3,7 @@ import PandaWebSocket from './core/sockets/PandaWebSocket'; ...@@ -3,7 +3,7 @@ import PandaWebSocket from './core/sockets/PandaWebSocket';
import PandaRecordWebSocket from './core/sockets/PandaRecordWebSocket'; import PandaRecordWebSocket from './core/sockets/PandaRecordWebSocket';
import Utils from './utils/Utils'; import Utils from './utils/Utils';
import PandaSparkWebSocket from './core/sockets/PandaSparkWebSocket'; import PandaSparkWebSocket from './core/sockets/PandaSparkWebSocket';
import Directive from './directive'; import Directive , {DirectiveTypes, AllDirectiveTypes} from './directive';
import AwakenDirective from './directive/AwakenDirective'; import AwakenDirective from './directive/AwakenDirective';
import ByeDirective from './directive/ByeDirective'; import ByeDirective from './directive/ByeDirective';
...@@ -15,5 +15,7 @@ export { ...@@ -15,5 +15,7 @@ export {
PandaSparkWebSocket, PandaSparkWebSocket,
Directive, Directive,
AwakenDirective, AwakenDirective,
ByeDirective ByeDirective,
DirectiveTypes,
AllDirectiveTypes
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment