customer_list/common/static/fonts/ywiconfont/iconfont.css | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
customer_list/common/static/fonts/ywiconfont/iconfont.ttf | 补丁 | 查看 | 原始文档 | blame | 历史 | |
customer_list/common/static/fonts/ywiconfont/iconfont.woff | 补丁 | 查看 | 原始文档 | blame | 历史 | |
customer_list/common/static/fonts/ywiconfont/iconfont.woff2 | 补丁 | 查看 | 原始文档 | blame | 历史 | |
package-lock.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
package.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/components/ChatContainer.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/components/playBar/PlayBar.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/components/playBar/hook/libs/duix.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/components/playBar/hook/useDigitalHuman.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/hooks/useSyncMsg.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
vite.config.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
customer_list/common/static/fonts/ywiconfont/iconfont.css
@@ -1,8 +1,8 @@ @font-face { font-family: "ywifont"; /* Project id 4655417 */ src: url('iconfont.woff2?t=1742799969257') format('woff2'), url('iconfont.woff?t=1742799969257') format('woff'), url('iconfont.ttf?t=1742799969257') format('truetype'); src: url('iconfont.woff2?t=1744017975539') format('woff2'), url('iconfont.woff?t=1744017975539') format('woff'), url('iconfont.ttf?t=1744017975539') format('truetype'); } .ywifont { @@ -13,6 +13,10 @@ -moz-osx-font-smoothing: grayscale; } .ywicon-shuziren:before { content: "\e64e"; } .ywicon-shengyin:before { content: "\e8b8"; } customer_list/common/static/fonts/ywiconfont/iconfont.ttfBinary files differ
customer_list/common/static/fonts/ywiconfont/iconfont.woffBinary files differ
customer_list/common/static/fonts/ywiconfont/iconfont.woff2Binary files differ
package-lock.json
@@ -37,6 +37,7 @@ "file-saver": "^2.0.5", "highlight.js": "^11.7.0", "html2canvas": "^1.4.1", "jose": "^6.0.10", "jquery": "^3.7.1", "js-cookie": "^3.0.1", "js-table2excel": "^1.0.3", @@ -5886,6 +5887,15 @@ "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "bin": { "jiti": "bin/jiti.js" } }, "node_modules/jose": { "version": "6.0.10", "resolved": "https://registry.npmmirror.com/jose/-/jose-6.0.10.tgz", "integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } }, "node_modules/jquery": { @@ -14643,6 +14653,11 @@ "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.6.tgz", "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==" }, "jose": { "version": "6.0.10", "resolved": "https://registry.npmmirror.com/jose/-/jose-6.0.10.tgz", "integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==" }, "jquery": { "version": "3.7.1", "resolved": "https://registry.npmmirror.com/jquery/-/jquery-3.7.1.tgz", package.json
@@ -54,6 +54,7 @@ "file-saver": "^2.0.5", "highlight.js": "^11.7.0", "html2canvas": "^1.4.1", "jose": "^6.0.10", "jquery": "^3.7.1", "js-cookie": "^3.0.1", "js-table2excel": "^1.0.3", src/components/chat/components/ChatContainer.vue
@@ -22,6 +22,21 @@ <i class="ywifont ywicon-xiangxiajiantou !text-[20px]" /> </div> </div> <div v-show="digitalHumanIsShow" class="absolute right-28 bottom-[250px] w-[400px] h-[540px]"> <span class="ywifont ywicon-guanbi text-[20px] cursor-pointer absolute top-2 right-2 z-[1]" @click="closeDigitalHuman"></span> <div class="duix-container h-full w-full"></div> </div> <div v-if="!digitalHumanIsShow" class="absolute right-28 bottom-[250px]"> <el-tooltip content="æ°å人" placement="top"> <div class="flex items-center justify-center size-[38px] cursor-pointer hover:text-[#0284ff] border rounded-full hover:bg-[#f6f7f9] shadow bg-white" @click="openDigitalHuman" > <i class="ywifont ywicon-shuziren !text-[20px]" /> </div> </el-tooltip> </div> <!-- è¾å ¥åºå --> <div class="w-full px-6 pb-6 bg-[rgb(247,248,250)] flex justify-center z-[1] flex-0" v-if="!isSharePage"> @@ -50,6 +65,7 @@ import { useChatWidth } from '../hooks/useChatWidth'; import { useScroll } from '../hooks/useScroll'; import emitter from '/@/utils/mitt'; import { useDigitalHuman } from './playBar/hook/useDigitalHuman'; const props = defineProps<{ loading?: boolean; @@ -58,7 +74,9 @@ }>(); const chatListDom = ref<HTMLDivElement>(); const { openDigitalHuman, isHumanTalking, digitalHumanIsShow, closeDigitalHuman } = useDigitalHuman({ container: '.duix-container', }); const { scrollToBottom, isBottom } = useScroll({ chatListDom, }); src/components/chat/components/playBar/PlayBar.vue
@@ -145,6 +145,7 @@ <span class="ywifont ywicon-fujian !text-[20px] font-bold"></span> </div> </el-tooltip> <el-tooltip v-if="isSupportSpeech" placement="top" :content="recordState.isRecording ? '忢è¯é³è¾å ¥' : 'è¯é³è¾å ¥'"> <div class="cursor-pointer size-[24px] relative !z-10 rounded flex-center hover:bg-[#f2f2f2]" @click="speechClick"> @@ -220,6 +221,7 @@ import MetricValuesPreview from './metricValues/MetricValuesPreview.vue'; import { useSpeech } from './hook/useSpeech'; import { useDigitalHuman } from './hook/useDigitalHuman'; const emits = defineEmits(['sendClick', 'stopGenClick']); const props = defineProps({ @@ -256,7 +258,9 @@ inputValue: inputValue, inputRef: inputRef, }); const { openDigitalHuman, isHumanTalking, digitalHumanIsShow } = useDigitalHuman({ container: '.duix-container', }); const clearTextarea = () => { inputValue.value = ''; }; @@ -324,6 +328,7 @@ pastTarget: inputRef as any, attachFileList: attachList, }); const deleteAttachInIndex = (index: number) => { const attach = attachList.value[index]; if (attach.type === 'file') { src/components/chat/components/playBar/hook/libs/duix.js
¶Ô±ÈÐÂÎļþ ÎļþÌ«´ó src/components/chat/components/playBar/hook/useDigitalHuman.ts
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,226 @@ import { nextTick, onDeactivated, onMounted, ref } from 'vue'; import { SignJWT } from 'jose'; import './libs/duix.js'; import { questionStreamByPost } from '/@/api/ai/chat'; import { activeGroupType, activeRoomId } from '/@/stores/chatRoom'; import { markdownToTxt } from 'markdown-to-txt'; export type UseDigitalHumanProps = { container: string; }; export const useDigitalHuman = (props: UseDigitalHumanProps) => { const { container } = props; const duixConfig = { appId: '1356792813207031808', appKey: '659b068e-900c-4fe5-bb96-3ca70fe0aae4', sign: '', conversationId: '1909088110274277378', /** @description è¿ææ¶é´ï¼å°æ¶ï¼ */ expired: 12, }; // æ¯å¦å·²æ¥å£ç¸åº const isReceiveRes = ref(false); const digitalHumanIsShow = ref(false); const closeDigitalHuman = () => { digitalHumanIsShow.value = false; resetDuixStatus(); }; const resetDuixStatus = () => { isReceiveRes.value = false; // isSpeaking.value = false; digitalHumanIsShow.value = false; duix?.stop(); }; const getPlainText = (item) => { let result = ''; const knowledgeText = item.knowledge.reduce((acc, cur) => { const mdText = cur.answer; const linkText = cur.metadata?.Title; if (linkText) { return `${mdText}\n\n${linkText}`; } return acc + mdText; }, ''); console.log('ð ~ knowledgeText:', knowledgeText); // const conclusionText = // item.conclusion // ?.filter((item) => !!item.report) // .map((item) => item.report) // .join('\n\n') ?? ''; // result += knowledgeText + conclusionText; result = knowledgeText; return markdownToTxt(result); }; let isWaitingSpeak = false; const initDuix = () => { const sign = duixConfig.sign; // signç±æå¡ç«¯çæ const conversationId = duixConfig.conversationId; // duixå¹³å°ä¼è¯id if (!sign || !conversationId) { return alert('åæ°ä¸è½ä¸ºç©º'); } duix.on('error', (data) => { console.error(data); }); duix.on('intialSucccess', () => { console.info('intialSucccess'); // æ¤æ¶åå§åæåï¼å¯è°ç¨start duix.start({ conversationId, openAsr: true }).then((res) => { console.info('start', res); }); }); duix.on('bye', (data) => { console.info('bye', data); }); duix.on('progress', (progress) => { console.info('progress', progress); }); duix.on('show', () => { console.info('show'); // æ¤æ¶å¯ç¡®è®¤è§é¢å·² // (document.querySelector('#modal') as HTMLElement).style.display = 'none'; }); duix.on('openAsrSuccess', () => { console.info('openAsrSuccess'); }); duix.on('asrClose', () => { console.info('asrClose'); }); duix.on('speakStart', (data) => { // isSpeaking.value = true; console.info('speakStart', data); }); duix.on('speakEnd', (data) => { isReceiveRes.value = false; }); duix.on('speakSection', (data) => { console.info('speakSection', data); }); duix.on('speakError', (data) => { console.info('speakError', data); }); duix.on('asrResult', (data) => { console.info('asrResult', data); if (isReceiveRes.value) { return; } let hasResult = false; isReceiveRes.value = true; try { // isWaitingSpeak = true; // duix.speak({ // content: 'å·²æ¶å°æ¨çé®é¢ï¼æ£å¨æèä¸...请ç¨ç', // }); questionStreamByPost( { question: data, history_group_id: activeRoomId.value, raw_mode: false, group_type: activeGroupType.value, is_digital_human: true, }, (chunkRes) => { if (chunkRes.mode === 'result' && chunkRes.value?.answer_type === 'knowledge') { const plainText = getPlainText(chunkRes.value); hasResult = true; duix.speak({ content: plainText, }); } if (!chunkRes.value?.json_ok && chunkRes.value?.err_code === 'MESSAGE') { hasResult = true; isWaitingSpeak = false; duix.speak({ content: chunkRes.value.json_msg, }); } if (chunkRes.mode === 'finish') { if (!hasResult) { isWaitingSpeak = false; duix.speak({ content: 'ææ¶æ æ³å£å¤´æè¿°ä½ æè¯´çé®é¢', }); } else { hasResult = false; } // isReceiveRes.value = false; } } ); } catch (error) { console.error(error); isReceiveRes.value = false; } }); duix.on('report', (data) => { // console.info('report', data) }); duix .init({ sign, containerLable: container, }) .then((data) => { console.info('init', data); }); }; let hasInitDuix = false; let duix: any; const openDigitalHuman = () => { digitalHumanIsShow.value = true; nextTick(async () => { duixConfig.sign = await createSig(duixConfig.appId, duixConfig.appKey, 60 * 60 * duixConfig.expired); if (!hasInitDuix) { hasInitDuix = true; duix = new DUIX(); initDuix(); } else { duix.start({ conversationId: duixConfig.conversationId, openAsr: true }).then((res) => { console.info('start', res); }); } }); }; function createSig(appId, appKey, sigExp) { const now = Math.floor(Date.now() / 1000); const expiresAt = now + sigExp; const sign = new SignJWT({ appId }) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt(now) .setExpirationTime(expiresAt) .sign(new TextEncoder().encode(appKey)); return sign; } onMounted(() => { window.addEventListener('beforeunload', () => { closeDigitalHuman(); }); }); onDeactivated(() => { closeDigitalHuman(); }); return { digitalHumanIsShow, openDigitalHuman, isHumanTalking: isReceiveRes, closeDigitalHuman, }; }; src/components/chat/hooks/useSyncMsg.ts
@@ -52,7 +52,10 @@ const groupId = unref(historyGroupId); const startGroupId = data?.history_group_id; if (groupId !== startGroupId) return; showTip(data); const isDigitalHuman = data?.is_digital_human; if (!isDigitalHuman) { showTip(data); } } if (data?.type === 'chat_history_id') { const groupId = unref(historyGroupId); vite.config.ts
@@ -58,7 +58,7 @@ host: '0.0.0.0', port: env.VITE_PORT as unknown as number, open: JSON.parse(env.VITE_OPEN), hmr: false, hmr: true, proxy: { '/events': { target: 'http://localhost:3000',