|
- <template>
- <view class="pages">
- <digital-human :width="0" rootStyle="position: fixed; left: 50%; top: 100px; transform: translateX(-50%);">
- </digital-human>
-
- <image class="bgs" src="https://qufang.oss-cn-beijing.aliyuncs.com/serverBg.jpg" mode="" />
-
- <view class="server-box">
- <view class="server-head">
- <image class="servehead" src="@/static/image/servehead.png" mode="" />
- <image class="voiceing" src="@/static/image/voiceing.png" mode="" />
- </view>
-
- <!-- 消息列表 -->
- <scroll-view class="scroll-box" scroll-y :scroll-into-view="scrollId">
- <!-- 循环体 -->
- <block v-for="(item, index) in messageList">
- <!-- 左侧 -->
- <view :class="item.direction" :key="index" :id="`scrollId${index}`">
- <view class="msg-text">
- <view class="msg-texts">
- {{ item.text }}
- </view>
- <image @tap="rePlayText(item.text)" v-if="item.direction == 'lside' && index !== 0 "
- class="voiceplay" src="@/static/image/voiceplay.png" mode="" />
- </view>
- <view class="timer">{{ item.time }}</view>
- </view>
- </block>
- </scroll-view>
- <view class="send-box">
-
- <view v-if="sendType" class="input-box">
- <input v-model="inputText" class="inputs" placeholder-class="placestyle" type="text"
- confirm-type="send" @confirm="sendChat" placeholder="输入你想咨询的问题或点击右边的麦克风和我聊天" />
- </view>
-
- <view v-else class="voice-box" @touchstart="touchStart" @touchend="touchEnd">
- <text>{{ voiceState }}</text>
- </view>
-
- <view class="change-type">
- <image @tap="changeSendType(false)" v-if="sendType" src="@/static/image/voiceInput.png" mode="" />
- <image @tap="changeSendType(true)" v-else src="@/static/image/textInput.png" mode="" />
- </view>
- </view>
- </view>
-
-
- <view v-if="recording" class="luyin">
- <image src="@/static/image/recordingIcon.png" mode="" />
- <text>正在录音...</text>
- </view>
-
- </view>
- </template>
-
- <script>
- const pluginConfig = { // 数字人配置
- "asrConfig": {
- "class": "TencentASR",
- "secretKey": "",
- "secretId": "AKIDyvwuvrfikhYK1dDj9Vlv154zmVAjFdt0",
- "appId": 1310500600,
- "partnerId": "0002",
- "signatureUrl": "https://commercial-integration-asr-int-api.xiaoice.com/api/v3/asr/tencentSignature",
- "hotWordId": "2ab7be50d11f11ecbfd6525400aec391",
- "silenceTime": 240,
- "engineModelType": "16k_zh",
- "needVad": 1,
- "filterDirty": 1,
- "filterModal": 0,
- "filterPunc": 1,
- "vadSilenceTime": 240,
- "convertNumMode": 1,
- "wordInfo": 2
- },
- "character": "chenzheling-yellow",
- "characterConfig": {
- "characters": {
- "chenzheling-yellow": {
- "name": "chenzheling-yellow-half",
- "id": "chenzheling-yellow",
- "frameWidth": 740,
- "frameHeight": 1260,
- "faceWidth": 190,
- "faceHeight": 300,
- "facePositionLeft": 450,
- "facePositionTop": 550,
- "facePositionLeftOffset": -180,
- "facePositionTopOffset": -430,
- "frameRate": 25,
- "defaultIdle": "A-idle",
- "idles": {
- "A-idle": {
- "id": "A-idle",
- "version": 6,
- "frameImageLength": 227,
- "frameImageType": "webp",
- "resetFrameSingleNum": 8,
- "resetFrames": [7, 15, 23, 31, 39, 47, 55, 63, 71, 79, 87, 95, 103, 111, 119, 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223],
- "frameImageBackupType": "png"
- }
- },
- "gestures": {},
- "crops": {}
- }
- },
- "sourcePath": "https://commercial-cdn.xiaoice.com/assets/images/characters",
- "batch": 16,
- "minLoadFrameNum": 100
- },
- "chatConfig": {
- "class": "CXHubChat",
- "url": "https://commercial-fab.xiaoice.com/api/hub/organizations/org-qufangwang/agents/64af7b6d6e1ba4813c4bd6ae/environments/draft/sessions",
- "defaultReply": [{
- "text": "对不起,我没听清~",
- "gesture": ""
- }],
- "csChatUrl": "https://commercial-fab.xiaoice.com/api/hub/organizations/org-qufangwang/characters//sessions"
- },
- "logConfig": {
- "class": "AliTraceLogger",
- "metaData": {
- "role": "prod",
- "organizationId": "org-qufangwang",
- "partnerId": "dh-api",
- "agentId": "64af7b6d6e1ba4813c4bd6ae"
- }
- },
- "talkConfig": {
- "class": "CarouselTalk",
- "url": "https://commercial-fab.xiaoice.com",
- "options": {
- "path": "/api/v1/carousel/avatars/chenzheling-yellow/socket.io",
- "wsPath": "/api/v1/carousel/avatars/chenzheling-yellow/websocket",
- "transports": ["websocket", "polling"],
- "query": "?image_format=.webp&voice_id=64af7b6da9d616bd022f6b69"
- },
- "maxRetryTime": 5,
- "retryTimeInterval": 100
- }
- }
- const plugin = requirePlugin('digital-human-plugin').api; // 数字人实例
- const plugins = requirePlugin("WechatSI");
- let manager = plugins.getRecordRecognitionManager();
-
-
- export default {
- data() {
- return {
- sendType: true, // true 为文字输入 false 为语音输入
- messageList: [], // 消息列表
- inputText: '', // 文字输入
- voiceState: '按住 说话', //
- recording: false, // 展示录音提示框
- scrollId: '', // 默认不滚动
- };
- },
-
- onLoad() {
- uni.showLoading({
- title: '加载中...',
- })
- plugin.init({
- ...pluginConfig,
- onReceivedAsrText: (text) => {},
- onSuccess: (res) => {
- console.log(res, 'onSuccess')
- uni.hideLoading()
- let text = 'Hi~我是专业房产顾问嘉欣,有问题可以问我呦,试试说句“你好”和我打个招呼吧'
- plugin.human?.talkAsync(text)
- this.messageList.push({
- text: text,
- direction: 'lside'
- })
- },
- onFailed: res => {
- uni.hideLoading()
- console.log(res, 'onFailed')
- },
- onError: (res) => {
- uni.hideLoading()
- console.log(res, 'onError')
- }
- })
-
- this.initRecord()
- },
-
- onShow() {
- // #ifdef MP-WEIXIN
-
- // 内存告警处理
- wx.onMemoryWarning((level) => {
- plugin.clearCache()
- })
- wx.setInnerAudioOption({
- // IOS静音键的情况下允许播放声音
- obeyMuteSwitch: false,
- // 播放声音时中断其他app端声音
- mixWithOther: false,
- })
- // #endif
- },
- onHide() {},
- methods: {
- /**
- * 切换输入方式
- */
- changeSendType(e) {
- this.sendType = e
- },
-
- // // 模拟聊天回复接口
- async getReply(text) {
- console.log(this.setUid())
- await uni.request({
- url: `${pluginConfig.chatConfig.url}/${this.setUid()}`,
- method: 'POST',
- data: {
- "text": text,
- "currentSceneInfo": {
- "sceneId": ""
- }
- },
- success: async (res) => {
- const replay = res.data.sceneInfo[0].audioText[0]
- await plugin.human?.interrupt()
- let arr = []
- arr.push({
- direction: 'rside',
- text: text,
- time: this.getTimeNow()
- })
- arr.push({
- direction: 'lside',
- text: res.data.sceneInfo[0].audioText[0],
- time: this.getTimeNow()
- })
- this.messageList = [...this.messageList, ...arr]
- this.inputText = ''
- this.scrollId = `scrollId${this.messageList.length-1}`
-
- await plugin.human?.talkAsync(replay)
- }
- })
- },
-
-
-
- /**
- * 重播当前对话
- */
- async rePlayText(e) {
- console.log(e)
- // 暂停当前对话(如果有)
- await plugin.human?.interrupt()
- await plugin.human?.talkAsync(e)
- },
-
-
- /**
- * 获取当前时间
- */
- getTimeNow() {
- let now = new Date();
- let year = now.getFullYear(); //获取完整的年份(4位,1970-????)
- let month = now.getMonth() + 1; //获取当前月份(0-11,0代表1月)
- let today = now.getDate(); //获取当前日(1-31)
- let hour = now.getHours(); //获取当前小时数(0-23)
- let minute = now.getMinutes(); //获取当前分钟数(0-59)
- let second = now.getSeconds(); //获取当前秒数(0-59)
- let nowTime = ''
- nowTime = year + '-' + this.fillZero(month) + '-' + this.fillZero(today) + ' ' + this.fillZero(hour) +
- ':' +
- this.fillZero(minute) + ':' + this.fillZero(second)
- return nowTime
- },
- // 补零
- fillZero(str) {
- var realNum;
- if (str < 10) {
- realNum = '0' + str;
- } else {
- realNum = str;
- }
- return realNum;
- },
-
-
- /**
- * 发送文字
- */
- async sendChat(e) {
- await this.getReply(e.detail.value)
- },
-
- /**
- * 长按识别
- */
- touchStart() {
- this.recording = true
- manager.start({
- duration: 60000,
- lang: "zh_CN"
- });
- },
-
- /**
- * 长按结束
- * */
- touchEnd() {
- this.recording = false
- manager.stop();
- },
- /**
- * 初始化语音识别回调
- * 绑定语音播放开始事件
- */
- initRecord() {
- manager.onStart = (res) => {
- this.voiceState = "正在录音"
- };
- //有新的识别内容返回,则会调用此事件
- manager.onRecognize = (res) => {}
-
- // 识别结束事件
- manager.onStop = async (res) => {
-
- if (res.result) {
- await this.getReply(res.result)
- }
-
- this.voiceState = "按住 说话"
- }
-
- // 识别错误事件
- manager.onError = (res) => {}
- },
-
- // 简单生成uid
- setUid() {
- let uid = new Date().getTime() + Math.random().toString(36).substr(2);
- return uid
- },
-
-
- }
- };
- </script>
- <style lang="scss" scoped>
- /* pages/intelligentvoiceassistant/intelligentvoiceassistant.wxss */
- view {
- box-sizing: border-box;
- }
-
- .pages {
- position: relative;
- width: 100vw;
- height: 100vh;
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
- }
-
- .bgs {
- position: absolute;
- z-index: 0;
- width: 100%;
- height: 100%;
- }
-
- .server-box {
- position: relative;
- z-index: 10;
- width: calc(100%-1rpx);
- height: 658rpx;
- background: rgba(0, 0, 0, 0.5);
- backdrop-filter: saturate(180%) blur(20px);
- border-radius: 32rpx 32rpx 0 0;
- border: 1rpx solid #FFFFFF;
- }
-
- .server-head {
- margin: -130rpx 0 0 0;
- padding: 0 32rpx;
- width: 100%;
- display: flex;
- align-items: flex-end;
- }
-
- .servehead {
- width: 192rpx;
- height: 192rpx;
- }
-
- .voiceing {
- margin-left: 12rpx;
- width: 78rpx;
- height: 50rpx;
- }
-
- /*
- .close {
- width: ;
- } */
-
-
- .scroll-box {
- width: 100%;
- height: calc(658rpx - 192rpx + 130rpx - 104rpx);
- }
-
- .lside {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- }
-
- .rside {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
- }
-
- .msg-texts {
- margin: 36rpx 32rpx 12rpx;
- padding: 16rpx 28rpx;
- background: #fff;
- border-radius: 20rpx;
- }
-
- .lside .voiceplay {
- margin: 0 32rpx 16rpx 16rpx;
- }
-
- .lside .msg-text {
- display: flex;
- align-items: flex-end;
- }
-
- .lside .msg-text .msg-texts {
- margin: 36rpx 16rpx 12rpx 32rpx;
- }
-
-
- .timer {
- margin: 0 32rpx;
- font-size: 24rpx;
- font-family: PingFangSC-Regular, PingFang SC;
- font-weight: 400;
- color: #FFFFFF;
- line-height: 33rpx;
- }
-
- .rside .msg-texts {
- background: #2671E2;
- color: #fff;
- }
-
- .send-box {
- padding: 0 24rpx;
- width: 100%;
- height: 104rpx;
- background: #5D6168;
- display: flex;
- align-items: center;
- /* justify-content: center; */
- }
-
- .change-type {
- margin: 0 0 0 24rpx;
- width: 72rpx;
- height: 72rpx;
- }
-
- .change-type image {
- width: 72rpx;
- height: 72rpx;
- }
-
- .input-box,
- .voice-box {
- width: 606rpx;
- height: 72rpx;
- background: #FFFFFF;
- border-radius: 12rpx;
- }
-
- .voice-box {
- display: flex;
- justify-content: center;
- align-items: center;
- }
-
- .voice-box text {
- font-size: 28rpx;
- font-family: PingFangSC-Medium, PingFang SC;
- font-weight: 500;
- color: #000000;
- line-height: 40rpx;
- }
-
- .inputs {
- padding: 0 28rpx;
- box-sizing: border-box;
- width: 100%;
- height: 100%;
- font-size: 22rpx;
- font-family: PingFangSC-Regular, PingFang SC;
- font-weight: 400;
- line-height: 30rpx;
- }
-
- .placestyle {
- font-size: 22rpx;
- font-family: PingFangSC-Regular, PingFang SC;
- font-weight: 400;
- color: #CCCCCC;
- line-height: 30rpx;
- }
-
-
- .luyin {
- position: fixed;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- z-index: 100;
- width: 280rpx;
- height: 258rpx;
- background: rgba(0, 0, 0, 0.7);
- border-radius: 20rpx;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- }
-
- .luyin image {
- margin: 0 0 20rpx 0;
- width: 46rpx;
- height: 62rpx;
- }
-
- .luyin text {
- font-size: 28rpx;
- font-family: PingFangSC-Regular, PingFang SC;
- font-weight: 400;
- color: #FFFFFF;
- line-height: 40rpx;
- }
-
- .voiceplay {
- flex-shrink: 0;
- width: 64rpx;
- height: 64rpx;
- }
- </style>
|