You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
manage/timer.html

373 lines
14 KiB

<!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()">&times;</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>