|
|
<!DOCTYPE html>
|
|
|
<html lang="zh-CN">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<title>健身房管理系统-用户</title>
|
|
|
<style>
|
|
|
* {
|
|
|
margin: 0;
|
|
|
padding: 0;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
body, html {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
display: flex;
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
|
font-family: Arial, sans-serif;
|
|
|
background-color: #f5f5f5;
|
|
|
}
|
|
|
.container {
|
|
|
width: 100vw;
|
|
|
height: 100vh;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
|
background-color: #fff;
|
|
|
transform-origin: center;
|
|
|
padding: 20px;
|
|
|
transform: scale(1);
|
|
|
}
|
|
|
.countdown-text {
|
|
|
font-size: 16px;
|
|
|
color: #666;
|
|
|
margin-bottom: 10px;
|
|
|
}
|
|
|
.countdown-time {
|
|
|
font-size: 32px;
|
|
|
font-weight: bold;
|
|
|
margin: 20px 0;
|
|
|
}
|
|
|
.button-group {
|
|
|
display: flex;
|
|
|
gap: 20px;
|
|
|
}
|
|
|
.button {
|
|
|
width: 60px;
|
|
|
height: 60px;
|
|
|
border-radius: 50%;
|
|
|
border: 2px solid #000;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
cursor: pointer;
|
|
|
font-size: 24px;
|
|
|
color: #000;
|
|
|
background-color: transparent;
|
|
|
}
|
|
|
/* 去掉点击或聚焦时的默认蓝色高亮 */
|
|
|
.button:focus {
|
|
|
outline: none;
|
|
|
}
|
|
|
.button:active {
|
|
|
background-color: transparent;
|
|
|
}
|
|
|
|
|
|
/* 样式调整,针对input按钮 */
|
|
|
input[type="button"] {
|
|
|
width: 80px;
|
|
|
height: 60px;
|
|
|
font-size: 24px;
|
|
|
color: #000;
|
|
|
background-color: transparent;
|
|
|
border: 2px solid #000;
|
|
|
border-radius: 5px;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
input[type="button"]:hover {
|
|
|
background-color: #f0f0f0;
|
|
|
}
|
|
|
.center-bold {
|
|
|
text-align: center; /* 文本居中 */
|
|
|
font-size: 1.2em; /* 增大字体大小 */
|
|
|
font-weight: bold; /* 加粗字体 */
|
|
|
}
|
|
|
/* 模态弹窗样式优化 */
|
|
|
.modal {
|
|
|
display: none; /* 默认隐藏 */
|
|
|
position: fixed; /* 固定位置 */
|
|
|
z-index: 1000; /* 在最上层 */
|
|
|
left: 0;
|
|
|
top: 0;
|
|
|
width: 100%; /* 全宽 */
|
|
|
height: 100%; /* 全高 */
|
|
|
overflow: auto; /* 允许滚动 */
|
|
|
background-color: rgba(0, 0, 0, 0.6); /* 更深的半透明背景 */
|
|
|
}
|
|
|
|
|
|
.modal-content {
|
|
|
background-color: #ffffff;
|
|
|
margin: 15% auto; /* 上下15%自动居中 */
|
|
|
padding: 20px;
|
|
|
border-radius: 8px; /* 圆角边框 */
|
|
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); /* 阴影效果 */
|
|
|
width: 80%; /* 宽度 */
|
|
|
max-width: 600px; /* 最大宽度 */
|
|
|
transition: all 0.3s ease; /* 平滑过渡效果 */
|
|
|
}
|
|
|
|
|
|
.alert-title {
|
|
|
font-size: 28px;
|
|
|
color: #d9534f;
|
|
|
text-align: center;
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
.modal-body {
|
|
|
font-size: 18px; /* 增大字体大小 */
|
|
|
line-height: 1.6; /* 设置行高,使文本行间距更舒适 */
|
|
|
}
|
|
|
|
|
|
.modal-body span {
|
|
|
display: block; /* 使每个span元素单独成行 */
|
|
|
margin-bottom: 10px; /* 添加底部外边距,增加行间距 */
|
|
|
}
|
|
|
|
|
|
.btn {
|
|
|
background-color: #d9534f;
|
|
|
color: white;
|
|
|
padding: 14px 20px;
|
|
|
border: none;
|
|
|
border-radius: 4px; /* 圆角按钮 */
|
|
|
cursor: pointer;
|
|
|
width: 100%;
|
|
|
font-size: 16px;
|
|
|
}
|
|
|
|
|
|
.btn:hover {
|
|
|
background-color: #c9302c; /* 悬停时颜色加深 */
|
|
|
}
|
|
|
body, html {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
display: flex;
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
|
font-family: Arial, sans-serif;
|
|
|
background-color: #f5f5f5;
|
|
|
background-image: url('/static/img/background.jpeg');
|
|
|
background-size: cover;
|
|
|
background-position: center;
|
|
|
background-repeat: no-repeat;
|
|
|
}
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="record-list" style="position: absolute; top: 0; left: 0; width: 100%; font-weight: bold; z-index: 10; display: flex; flex-direction: column; padding: 10px;">
|
|
|
<div class="record-item" th:each="rel:${TargetRecord}" style="display: flex; flex-direction: column; margin-bottom: 10px; background-color: #fff; padding: 5px;">
|
|
|
<span class="center-bold">预期锻炼目标:</span>
|
|
|
<span class="center-bold">项目:<a class="record-cell" th:text="${rel.getTpName()}"></a></span>
|
|
|
<span class="center-bold">器材:<a class="record-cell" th:text="${rel.getTepName()}"></a></span>
|
|
|
<span class="center-bold">地点:<a class="record-cell" th:text="${rel.getTrLocation()}"></a></span>
|
|
|
<span class="center-bold">时长:<a class="record-cell" th:text="${rel.getTTime()}"></a></span>
|
|
|
<!-- <span class="center-bold">时间:<a class="record-cell" th:text="${rel.getTDate()}"></a></span>-->
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="container">
|
|
|
<div class="countdown-text">距离锻炼开始已经过</div>
|
|
|
<div class="countdown-time" id="countdown">00小时00分00秒</div>
|
|
|
<div class="button-group" th:each="rel:${TargetRecord}">
|
|
|
<input type="button" value="开始" id="playButton">
|
|
|
<a th:attr="data-rpName=${rel.getTpName()}, data-repName=${rel.getTepName()}, data-rrLocation=${rel.getTrLocation()}" id="endLink">
|
|
|
<input type="button" value="结束" id="endButton" th:action="@{/user/addTargetRecord}">
|
|
|
</a>
|
|
|
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 模态弹窗内容 -->
|
|
|
<div id="myModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeModal()">×</span>
|
|
|
<div class="alert-title">警报!</div>
|
|
|
<div class="modal-body" th:each="rel:${TargetRecord}">
|
|
|
<span>器材:<a th:text="${rel.getTepName()}"></a></span>
|
|
|
<span>地点:<a th:text="${rel.getTrLocation()}"></a></span>
|
|
|
</div>
|
|
|
<button class="btn" onclick="closeModal()">确认收到</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<script>
|
|
|
let countdownTime = 0; // 从0秒开始
|
|
|
let countdownInterval = null;
|
|
|
let isCounting = false; // 用于跟踪倒计时是否在进行中
|
|
|
let pausedTime = null; // 用于存储暂停时的时间
|
|
|
|
|
|
// 将秒数转换为"XX小时XX分XX秒"格式
|
|
|
function formatTime(seconds) {
|
|
|
const hrs = Math.floor(seconds / 3600);
|
|
|
const mins = Math.floor((seconds % 3600) / 60);
|
|
|
const secs = seconds % 60;
|
|
|
return `${String(hrs).padStart(2, '0')}小时${String(mins).padStart(2, '0')}分${String(secs).padStart(2, '0')}秒`;
|
|
|
}
|
|
|
|
|
|
// 更新倒计时显示
|
|
|
function updateCountdown() {
|
|
|
document.getElementById("countdown").textContent = formatTime(countdownTime);
|
|
|
}
|
|
|
|
|
|
// 启动或暂停计时
|
|
|
function toggleCountdown() {
|
|
|
const playButton = document.getElementById("playButton");
|
|
|
|
|
|
if (isCounting) {
|
|
|
// 暂停时记录当前时间
|
|
|
pausedTime = countdownTime; // 保存当前时间
|
|
|
clearInterval(countdownInterval); // 停止计时器
|
|
|
playButton.value = "开始"; // 切换回播放按钮
|
|
|
isCounting = false;
|
|
|
// 关闭语音识别
|
|
|
if (recognition) {
|
|
|
recognition.stop();
|
|
|
}
|
|
|
} else {
|
|
|
countdownInterval = setInterval(() => {
|
|
|
countdownTime++; // 每秒增加1
|
|
|
updateCountdown();
|
|
|
}, 1000);
|
|
|
playButton.value = "暂停"; // 切换为暂停按钮
|
|
|
isCounting = true;
|
|
|
// 开启语音识别
|
|
|
startVoiceRecognition();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
// 结束计时并跳转页面
|
|
|
function endCountdown() {
|
|
|
if (confirm("确定要结束计时吗?")) {
|
|
|
clearInterval(countdownInterval); // 停止计时器
|
|
|
var endTime = countdownTime; // 获取结束时的时长
|
|
|
countdownTime = 0; // 重置计时器
|
|
|
updateCountdown(); // 更新显示为0
|
|
|
isCounting = false;
|
|
|
document.getElementById("playButton").value = "开始"; // 重置播放按钮
|
|
|
|
|
|
// 获取结束时的时长,并转换为格式化的时间字符串
|
|
|
var formattedEndTime = formatTime(endTime);
|
|
|
|
|
|
// 构建完整的URL,并设置<a>标签的href属性
|
|
|
var endLink = document.getElementById("endLink");
|
|
|
var rpName = endLink.getAttribute("data-rpName");
|
|
|
var repName = endLink.getAttribute("data-repName");
|
|
|
var rrLocation = endLink.getAttribute("data-rrLocation");
|
|
|
var url = `/user/addRealRecord?rpName=${encodeURIComponent(rpName)}&repName=${encodeURIComponent(repName)}&rrLocation=${encodeURIComponent(rrLocation)}&rTime=${formattedEndTime}`;
|
|
|
endLink.href = url;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 显示模态弹窗的函数
|
|
|
function showModal() {
|
|
|
var modal = document.getElementById("myModal");
|
|
|
modal.style.display = "block";
|
|
|
}
|
|
|
|
|
|
// 关闭模态弹窗的函数
|
|
|
function closeModal() {
|
|
|
var modal = document.getElementById("myModal");
|
|
|
modal.style.display = "none";
|
|
|
}
|
|
|
|
|
|
// 检查时间并显示弹窗的逻辑
|
|
|
function checkTimeAndAlert() {
|
|
|
if (countdownTime >= 10) { // 当时间达到10秒时
|
|
|
showModal(); // 显示弹窗
|
|
|
clearInterval(checkTimeAndAlertInterval); // 停止检查时间的间隔
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 设置检查时间的间隔
|
|
|
let checkTimeAndAlertInterval = setInterval(checkTimeAndAlert, 1000);
|
|
|
|
|
|
// 绑定结束按钮点击事件
|
|
|
document.getElementById("endButton").addEventListener("click", endCountdown);
|
|
|
|
|
|
// 绑定播放/暂停按钮点击事件
|
|
|
document.getElementById("playButton").addEventListener("click", toggleCountdown);
|
|
|
|
|
|
// 页面加载时初始化计时
|
|
|
updateCountdown();
|
|
|
|
|
|
let recognition;
|
|
|
|
|
|
function startVoiceRecognition() {
|
|
|
if (!('SpeechRecognition' in window) && !('webkitSpeechRecognition' in window)) {
|
|
|
alert("浏览器不支持语音识别功能,请使用支持该功能的浏览器!");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 在这里使用 Google Cloud Speech-to-Text API
|
|
|
const audioStream = new MediaStream();
|
|
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
|
const analyser = audioContext.createAnalyser();
|
|
|
|
|
|
// 使用 Web Audio API 获取麦克风音频流
|
|
|
navigator.mediaDevices.getUserMedia({ audio: true })
|
|
|
.then(function(stream) {
|
|
|
const mediaStreamSource = audioContext.createMediaStreamSource(stream);
|
|
|
mediaStreamSource.connect(analyser);
|
|
|
|
|
|
// 创建 Google Cloud API 需要的语音识别请求体
|
|
|
const request = {
|
|
|
config: {
|
|
|
encoding: 'LINEAR16',
|
|
|
sampleRateHertz: 16000,
|
|
|
languageCode: 'zh-CN',
|
|
|
},
|
|
|
audio: {
|
|
|
content: "" // 将要转换的音频数据
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
// 请求音频流,发送到 Google Cloud Speech-to-Text API
|
|
|
const reader = new FileReader();
|
|
|
reader.onloadend = function() {
|
|
|
request.audio.content = reader.result.split(',')[1]; // Base64 编码的音频数据
|
|
|
fetch('https://speech.googleapis.com/v1p1beta1/speech:recognize?key=YOUR_GOOGLE_API_KEY', {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
},
|
|
|
body: JSON.stringify(request),
|
|
|
})
|
|
|
.then(response => response.json())
|
|
|
.then(data => {
|
|
|
if (data.results && data.results.length > 0) {
|
|
|
const transcript = data.results[0].alternatives[0].transcript;
|
|
|
console.log('识别结果:', transcript);
|
|
|
|
|
|
// 判断是否包含“救命”
|
|
|
if (transcript.includes("救命")) {
|
|
|
alert("紧急情况!请立即寻求帮助!");
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
.catch(error => console.error('语音识别失败:', error));
|
|
|
};
|
|
|
|
|
|
// 从音频流中获取数据
|
|
|
const audioChunks = [];
|
|
|
const mediaRecorder = new MediaRecorder(stream);
|
|
|
mediaRecorder.ondataavailable = event => audioChunks.push(event.data);
|
|
|
mediaRecorder.onstop = () => reader.readAsDataURL(new Blob(audioChunks));
|
|
|
|
|
|
mediaRecorder.start();
|
|
|
recognition = mediaRecorder;
|
|
|
})
|
|
|
.catch(error => {
|
|
|
console.error("无法获取麦克风权限:", error);
|
|
|
});
|
|
|
}
|
|
|
startVoiceRecognition();
|
|
|
|
|
|
</script>
|
|
|
</body>
|
|
|
</html>
|