html_digital_item/simple.html
Song367 fb96576ebf
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 41s
修改后端api配置
2025-09-28 10:42:19 +08:00

624 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximu-scale=1.0,user-scalable=no" />
<title>Yantu Digital human</title>
<link rel="icon" type="image/png" sizes="48x48" href="./yantu/favicon.png" />
<style>
:root {
--primary-100: #FF7F50;
--primary-200: #dd6236;
--primary-300: #8f1e00;
--accent-100: #8B4513;
--accent-200: #ffd299;
--text-100: #000000;
--text-200: #2c2c2c;
--bg-100: #F7EEDD;
--bg-200: #ede4d3;
--bg-300: #c4bcab;
}
html,
body {
margin: 0;
padding: 0;
font-family: 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: var(--bg-100);
}
.modal {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
overflow: auto;
background: rgba(247, 238, 221, 0.95);
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background-color: transparent;
padding: 20px;
width: auto;
border-radius: 0;
box-shadow: none;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
.container {
width: 100vw;
height: 100vh;
background: var(--bg-200);
}
.item {
margin-bottom: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.item>span {
font-size: 24px;
font-weight: 600;
margin-bottom: 20px;
color: var(--text-200);
}
.input {
background: var(--bg-100);
height: 48px;
border: 1px solid var(--accent-100);
outline: none;
border-radius: 8px;
padding: 0 16px;
width: 100%;
box-sizing: border-box;
font-size: 16px;
transition: all 0.3s ease;
color: var(--text-200);
margin-bottom: 10px;
}
.input:focus {
border-color: var(--primary-100);
box-shadow: 0 0 0 3px var(--accent-200);
}
.btn {
border: none;
border-radius: 8px;
height: 48px;
background: var(--primary-100);
color: white;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: all 0.3s ease;
padding: 0 24px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(0,0,0,0.15);
background: var(--primary-200);
}
.btn-config {
position: absolute;
top: 20px;
right: 20px;
background: var(--accent-100);
color: white;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
font-size: 20px;
cursor: pointer;
transition: all 0.3s ease;
transform: rotate(0deg);
}
.btn-config:hover {
transform: rotate(90deg);
background: var(--primary-200);
}
.btn:active {
transform: translateY(0);
}
.statement {
position: absolute;
top: 42%;
left: 80%;
transform: translate(-50%);
color: #fff;
padding: 12px;
box-sizing: border-box;
text-align: center;
background-color: rgba(27, 160, 53, 0.5);
border-radius: 12px;
visibility: hidden;
z-index: 100000;
}
.statement:empty {
display: none;
}
.subtitle {
position: absolute;
top: 50%;
left: 40%;
transform: translateX(-50%);
color: #000;
padding: 12px;
box-sizing: border-box;
text-align: center;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 12px;
visibility: hidden;
z-index: 100000;
}
.subtitle:empty {
display: none;
}
/* 图片框样式 */
.image-panel {
position: absolute;
top: 70%;
left: 6%;
transform: translateY(-50%);
display: none;
flex-direction: column;
gap: 20px;
z-index: 100000;
}
.image-container-square {
width: 16vw;
height: 16vw;
border-radius: 8px;
overflow: hidden;
/* background: rgba(255, 255, 255, 0.1); */
display: flex;
align-items: center;
justify-content: center;
}
.image-container-vertical {
width: 64vw;
height: 78vw;
border-radius: 8px;
overflow: hidden;
/* background: rgba(255, 255, 255, 0.1); */
display: flex;
align-items: center;
justify-content: center;
}
.image-container-square img,
.image-container-vertical img {
width: 100%;
height: 100%;
object-fit: cover;
}
.image-placeholder {
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
text-align: center;
}
.icon-btn {
background: rgba(255,255,255,0.7);
border: none;
border-radius: 50%;
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 20px;
left: 20px;
z-index: 100001;
cursor: pointer;
transition: background 0.2s;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
padding: 0;
}
.icon-btn + .icon-btn {
left: 74px;
}
.icon-btn:hover {
background: #eee;
}
.icon-btn img {
pointer-events: none;
}
</style>
<script src="https://cdn.bootcss.com/eruda/1.3.2/eruda.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-base64@3.7.2/base64.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/crypto-js.min.js"></script>
<script>eruda.init()</script>
<script src="./duix/duix.js"></script>
<!-- <script src="./yantu/yantoo-ava.js"></script> -->
</head>
<body>
<!-- 数字人容器 -->
<div class="container">
<!-- 图片框 -->
<div class="image-panel">
<div class="image-container-square">
<img src="./yantu/erweima.jpg" alt="图片1">
</div>
<div class="image-container-vertical">
<img src="./yantu/wenzi2.png" alt="图片2">
</div>
</div>
</div>
<div class="statement"></div>
<div class="subtitle"></div>
<!-- 全屏图标按钮 -->
<button id="fullscreenBtn" class="icon-btn" title="全屏">
<img src="./yantu/quanping.svg" alt="全屏" width="28" height="28">
</button>
<!-- 退出全屏图标按钮 -->
<button id="exitFullscreenBtn" class="icon-btn" title="退出全屏" style="display: none;">
<img src="./yantu/out_quan.svg" alt="退出全屏" width="28" height="28">
</button>
<!-- <input class="input" style="display: block;border-color: #8f1e00;z-index: 999;" id="duihua" placeholder="输入对话" value=""> -->
<div id="modal" class="modal">
<div class="modal-content" style="text-align: center; padding: 20px; position: relative;">
<button id="configBtn" class="btn-config" onclick="toggleConfig()">⚙️</button>
<div id="configSection" style="display: none;">
<!-- <div style="margin-bottom: 8px; width: 100%; text-align: left; color: var(--text-200);">签名密钥</div> -->
<input class="input" style="display: none;" id="sign" placeholder="请输入您的签名密钥" value="" />
<input class="input" style="display: none;" id="audio" placeholder="音色" value="" />
<div style="margin-bottom: 8px; width: 100%; text-align: left; color: var(--text-200);">性别</div>
<select class="input" id="gender" style="margin-bottom: 16px;">
<option value="male" selected></option>
<option value="female" ></option>
</select>
<select class="input" id="answer_type" style="margin-bottom: 16px;">
<option value="jiguang">极光</option>
<option value="gongzheng" selected>公证</option>
</select>
<div style="display: none;margin-bottom: 8px; width: 100%; text-align: left; color: var(--text-200);">会话ID</div>
<input class="input" id="conversationId" style="display: none;" placeholder="请输入会话ID" value="1920410565458886658" title="请输入会话ID" />
</div>
<button id="start" onclick="init()" class="btn" style="width: 200px; margin: 20px auto 0;">
<span>开始智能会话</span>
</button>
</div>
</div>
<script>
// 全屏
document.getElementById('fullscreenBtn').onclick = function() {
if (document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen();
} else if (document.documentElement.webkitRequestFullscreen) { // Safari
document.documentElement.webkitRequestFullscreen();
} else if (document.documentElement.msRequestFullscreen) { // IE11
document.documentElement.msRequestFullscreen();
}
};
// 退出全屏
document.getElementById('exitFullscreenBtn').onclick = function() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) { // Safari
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) { // IE11
document.msExitFullscreen();
}
};
// 监听全屏状态变化,切换按钮显示
document.addEventListener('fullscreenchange', function() {
if (document.fullscreenElement) {
document.getElementById('fullscreenBtn').style.display = 'none';
document.getElementById('exitFullscreenBtn').style.display = 'inline-block';
} else {
document.getElementById('fullscreenBtn').style.display = 'inline-block';
document.getElementById('exitFullscreenBtn').style.display = 'none';
}
});
const duix = new DUIX()
const sex = {
"male": "1920410565458886658",
"female": "1933000305591988225"
}
let conversationL = ""
const sex_audio = {
"male": "gongzheng-v5",
"female": "presenter_female"
}
const audio = document.getElementById('audio')
// Add event listener for gender selection
document.getElementById('gender').addEventListener('change', function(e) {
const selectedGender = e.target.value;
const conversationIdInput = document.getElementById('conversationId');
conversationIdInput.value = sex[selectedGender];
audio.value = sex_audio[selectedGender]
});
let trigger_status = true
let end_talk = ""
// Set initial conversation ID based on default gender
document.addEventListener('DOMContentLoaded', function() {
const genderSelect = document.getElementById('gender');
const conversationIdInput = document.getElementById('conversationId');
conversationIdInput.value = sex[genderSelect.value];
audio.value = sex_audio[genderSelect.value]
});
async function getToken() {
try {
const response = await fetch('https://14.103.114.237/gongzheng-backend/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
return data.token;
} catch (error) {
console.error('Error getting token:', error);
throw error;
}
}
function toggleConfig() {
const configSection = document.getElementById('configSection');
if (configSection.style.display === 'none') {
configSection.style.display = 'block';
} else {
configSection.style.display = 'none';
}
}
async function init() {
let sign = document.querySelector('#sign').value;
const conversationId = document.querySelector('#conversationId').value;
if (!sign) {
try {
sign = await getToken();
document.querySelector('#sign').value = sign;
} catch (error) {
alert('获取token失败请重试');
return;
}
}
if (!conversationId) {
return alert('会话ID不能为空');
}
duix.on('error', data => {
console.error(data)
})
duix.on('intialSucccess', () => {
console.info('intialSucccess')
// 此时初始化成功可调用start
duix.start({ conversationId, openAsr: true, useActSection: 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').style.display = "none"
document.querySelector('.image-panel').style.display = "flex"
document.querySelector('.statement').style.visibility = "visible"
document.querySelector('.subtitle').style.visibility = "visible"
})
duix.on('openAsrSuccess', () => {
console.info('openAsrSuccess')
})
duix.on('asrClose', () => {
console.info('asrClose')
})
// duix.on('speakStart', (data) => {
// console.info('speakStart', data)
// })
// duix.on('speakEnd', (data) => {
// console.info('speakEnd', data)
// })
duix.on('speakSection', (data) => {
console.info('speakSection', data)
document.querySelector('.image-panel').style.display = 'none'
document.querySelector('.subtitle').innerText = document.querySelector('.subtitle').innerText + data.content
// document.querySelector('.statement').innerText = all_answer + data;
})
duix.on('asrStop', (data) => {
console.info('asrStop', data)
// document.querySelector('.subtitle').innerText = ''
// document.querySelector('.statement').innerText = ''
});
duix.on('speakError', (data) => {
console.info('speakError', data)
})
duix.on('asrResult', async (data) => {
// document.querySelector('.statement').innerText = ''
// document.querySelector('.subtitle').innerText = ''
document.querySelector('.statement').innerText = data;
console.info('asrResult', data, trigger_status);
if (data.includes("暂停说话") || data.includes("暂停播放")){
duix.break()
trigger_status = true
return
}
if (trigger_status) {
try {
document.querySelector('.subtitle').innerText = ''
document.querySelector('.image-panel').style.display = 'flex'
// document.querySelector('.statement').innerText = ''
trigger_status = false;
let plu = document.getElementById('answer_type')
api_data = {}
if (plu.value=="jiguang"){
api_data = {
"model": "bot-20250522162100-44785",
"llm_type": "ours",
"conversation_id": conversationL,
"audio": audio.value,
"stream": true,
"stream_options": {"include_usage": true},
"messages": [
{
"role": "system",
"content": "你是一个知识渊博的马克思主义研究者,能够深入且清晰地阐述马克思主义相关的理论、思想等内容,以通俗易懂的语言向用户讲解,还能结合实际案例来加深用户对马克思主义的理解。"
},
{
"role": "user",
"content": data
}
]
}
} else{
api_data = {
query: data,
response_mode: 'streaming',
user: 'SYS002',
conversation_id: conversationL,
audio: audio.value
}
}
const response = await fetch('https://14.103.114.237/gongzheng-backend/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(api_data)
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let pre_talk = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data:')) {
const responseData = JSON.parse(line.slice(5).trim());
if (responseData.audio_data) {
conversationL = responseData.conversation_id
console.log("执行输入answer")
pre_talk = responseData.answer
duix.speak({
content: responseData.answer,
audio: responseData.audio_data
})
}
// Handle end of stream
if (responseData.isEnd) {
console.log("pretalk : ", pre_talk, "---------", trigger_status)
end_talk = pre_talk
// trigger_status = true
break;
}
}
}
}
} catch (error) {
console.error('Error in chat API:', error);
}
}
// test = [
// 'https://p3-bot-sign.byteimg.com/tos-cn-i-v4nquku3lp/0f1be92489b849f3b7568e9b17334e42.wav~tplv-v4nquku3lp-image.image?rk3s=68e6b6b5&x-expires=1752754758&x-signature=rXa5LFcZttKGZpHMVIP6SgZTsRI%3D','https://p6-bot-sign.byteimg.com/tos-cn-i-v4nquku3lp/9898244b02be4765a964cf4b3d4afcd0.wav~tplv-v4nquku3lp-image.image?rk3s=68e6b6b5&x-expires=1752754726&x-signature=%2BbIrCv%2B5yAUxZvNqBURCnSRVqWo%3D','https://p9-bot-sign.byteimg.com/tos-cn-i-v4nquku3lp/4bffded0446648a2aef1d2d7e59ddd0c.wav~tplv-v4nquku3lp-image.image?rk3s=68e6b6b5&x-expires=1752754642&x-signature=E5Dr2R%2Fhd%2Ft7K593ka393OlCPNg%3D']
// for (let index = 0; index < test.length; index++) {
// const element = test[index];
// duix.speak({content: "我", audio: element})
// }
});
duix.on('report', data => {
// console.info('report', data)
})
duix.on('speakEnd', async(data) => {
console.info('公证speakEnd: ', data["content"], "----------------", end_talk, "-----------------", trigger_status)
if(end_talk == data["content"]){
trigger_status = true
document.querySelector('.image-panel').style.display = 'flex'
document.querySelector('.subtitle').innerText = ''
document.querySelector('.statement').innerText = ''
}
})
duix.init({
sign,
containerLable: '.container'
}).then(data => {
console.info('init', data)
})
}
window.addEventListener('beforeunload', function(event) {
if (duix) {
duix.destroy()
duix.stop()
}
});
</script>
</body>
</html>