MassageRobot_Dobot/UI_next/templates/massage_plan_control.html
2025-05-27 15:46:31 +08:00

605 lines
23 KiB
HTML

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>按摩计划控制</title>
<style>
body {
margin: 0;
padding: 20px;
font-family: "Microsoft YaHei", sans-serif;
background-color: #f5f5f5;
}
.control-panel {
max-width: 600px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.mode-selector {
margin-bottom: 20px;
}
.mode-buttons {
display: flex;
gap: 10px;
margin-top: 10px;
}
.mode-button {
padding: 10px 20px;
border: none;
border-radius: 5px;
background-color: #e0e0e0;
cursor: pointer;
transition: all 0.3s ease;
}
.mode-button.active {
background-color: rgba(147, 112, 219, 0.8);
color: white;
}
.selected-points {
margin-top: 20px;
padding: 15px;
background-color: #f8f8f8;
border-radius: 5px;
}
.point-info {
margin: 5px 0;
color: #666;
}
.clear-button {
margin-top: 15px;
padding: 8px 15px;
border: none;
border-radius: 5px;
background-color: #ff4444;
color: white;
cursor: pointer;
transition: all 0.3s ease;
}
.clear-button:hover {
background-color: #ff6666;
}
.ellipse-controls {
margin-top: 20px;
padding: 15px;
background-color: #f8f8f8;
border-radius: 5px;
display: none;
}
.ellipse-controls.active {
display: block;
}
.control-group {
margin: 10px 0;
}
.control-group label {
display: block;
margin-bottom: 5px;
color: #666;
}
.control-group input[type="range"] {
width: 100%;
margin: 5px 0;
}
.control-group input[type="number"] {
width: 60px;
padding: 5px;
margin-left: 10px;
}
.direction-selector {
display: flex;
gap: 10px;
margin-top: 5px;
}
.direction-button {
padding: 5px 10px;
border: 1px solid #ddd;
border-radius: 3px;
background: white;
cursor: pointer;
}
.direction-button.active {
background-color: rgba(147, 112, 219, 0.8);
color: white;
border-color: rgba(147, 112, 219, 0.8);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.body-part-selector {
margin-bottom: 20px;
padding: 15px;
background-color: #f0f8ff;
border-radius: 5px;
}
.body-part-buttons {
display: flex;
gap: 10px;
margin-top: 10px;
}
.body-part-button {
padding: 10px 20px;
border: none;
border-radius: 5px;
background-color: #e0e0e0;
cursor: pointer;
transition: all 0.3s ease;
}
.body-part-button.active {
background-color: #4682b4;
color: white;
}
</style>
</head>
<body>
<div class="control-panel">
<div class="body-part-selector">
<h3>身体部位选择</h3>
<div class="body-part-buttons">
<button class="body-part-button active" data-body-part="back">背部</button>
<button class="body-part-button" data-body-part="belly">腹部</button>
</div>
</div>
<div class="mode-selector">
<h3>绘制模式选择</h3>
<div class="mode-buttons">
<button class="mode-button active" data-mode="none"></button>
<button class="mode-button" data-mode="line">直线</button>
<button class="mode-button" data-mode="ellipse">椭圆</button>
<button class="mode-button" data-mode="lemniscate">8字形</button>
<button class="mode-button" data-mode="cycloid">摆线</button>
<button class="mode-button" data-mode="in_spiral">内螺旋</button>
<button class="mode-button" data-mode="out_spiral">外螺旋</button>
</div>
</div>
<div class="ellipse-controls" id="ellipseControls">
<h4>椭圆参数设置</h4>
<div class="control-group">
<label>宽度像素 (10-100): <span id="widthValue">30</span>px</label>
<input type="range" id="widthSlider" min="10" max="100" step="5" value="30">
</div>
<div class="control-group">
<label>圈数 (1-5): <span id="cyclesValue">1</span></label>
<input type="range" id="cyclesSlider" min="1" max="5" step="1" value="1">
</div>
<div class="control-group">
<label>方向:</label>
<div class="direction-selector">
<button class="direction-button active" data-direction="CW">顺时针</button>
<button class="direction-button" data-direction="CCW">逆时针</button>
</div>
</div>
</div>
<div class="ellipse-controls" id="lemniscateControls">
<h4>8字形参数设置</h4>
<div class="control-group">
<label>宽度像素 (10-100): <span id="lemniscateWidthValue">30</span>px</label>
<input type="range" id="lemniscateWidthSlider" min="10" max="100" step="5" value="30">
</div>
<div class="control-group">
<label>圈数 (1-5): <span id="lemniscateCyclesValue">1</span></label>
<input type="range" id="lemniscateCyclesSlider" min="1" max="5" step="1" value="1">
</div>
<div class="control-group">
<label>方向:</label>
<div class="direction-selector">
<button class="direction-button active" data-direction="CW">顺时针</button>
<button class="direction-button" data-direction="CCW">逆时针</button>
</div>
</div>
</div>
<div class="ellipse-controls" id="cycloidControls">
<h4>摆线参数设置</h4>
<div class="control-group">
<label>宽度像素 (10-100): <span id="cycloidWidthValue">30</span>px</label>
<input type="range" id="cycloidWidthSlider" min="10" max="100" step="5" value="30">
</div>
<div class="control-group">
<label>圈数 (1-5): <span id="cycloidCyclesValue">5</span></label>
<input type="range" id="cycloidCyclesSlider" min="1" max="10" step="1" value="5">
</div>
<div class="control-group">
<label>方向:</label>
<div class="direction-selector">
<button class="direction-button active" data-direction="CW">顺时针</button>
<button class="direction-button" data-direction="CCW">逆时针</button>
</div>
</div>
</div>
<div class="ellipse-controls" id="inSpiralControls">
<h4>内螺旋参数设置</h4>
<div class="control-group">
<label>宽度像素 (10-100): <span id="inSpiralWidthValue">30</span>px</label>
<input type="range" id="inSpiralWidthSlider" min="10" max="100" step="5" value="30">
</div>
<div class="control-group">
<label>圈数 (1-5): <span id="inSpiralCyclesValue">1</span></label>
<input type="range" id="inSpiralCyclesSlider" min="1" max="5" step="1" value="1">
</div>
<div class="control-group">
<label>方向:</label>
<div class="direction-selector">
<button class="direction-button active" data-direction="CW">顺时针</button>
<button class="direction-button" data-direction="CCW">逆时针</button>
</div>
</div>
</div>
<div class="ellipse-controls" id="outSpiralControls">
<h4>外螺旋参数设置</h4>
<div class="control-group">
<label>宽度像素 (10-100): <span id="outSpiralWidthValue">30</span>px</label>
<input type="range" id="outSpiralWidthSlider" min="10" max="100" step="5" value="30">
</div>
<div class="control-group">
<label>圈数 (1-5): <span id="outSpiralCyclesValue">1</span></label>
<input type="range" id="outSpiralCyclesSlider" min="1" max="5" step="1" value="1">
</div>
<div class="control-group">
<label>方向:</label>
<div class="direction-selector">
<button class="direction-button active" data-direction="CW">顺时针</button>
<button class="direction-button" data-direction="CCW">逆时针</button>
</div>
</div>
</div>
<div class="selected-points">
<h4>已选择的点</h4>
<div class="point-info" id="point1">第一个点:未选择</div>
<div class="point-info" id="point2">第二个点:未选择</div>
<button class="clear-button" id="clearPoints">清除选择</button>
</div>
</div>
<script>
// 创建广播通道
const channel = new BroadcastChannel('massage_plan_channel');
let currentMode = 'none';
let currentBodyPart = '{{ body_part }}'; // 使用服务器传递的参数
let selectedPoints = [];
let ellipseParams = {
width: 30,
cycles: 1,
direction: 'CW'
};
let lemniscateParams = {
width: 30,
cycles: 1,
direction: 'CW'
};
let cycloidParams = {
width: 30,
cycles: 5,
direction: 'CW'
};
let inSpiralParams = {
width: 30,
cycles: 1,
direction: 'CW'
};
let outSpiralParams = {
width: 30,
cycles: 1,
direction: 'CW'
};
// 初始化身体部位按钮状态
document.addEventListener('DOMContentLoaded', () => {
// 设置初始身体部位按钮状态
document.querySelectorAll('.body-part-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.bodyPart === currentBodyPart);
});
});
// 初始化身体部位选择按钮事件
document.querySelectorAll('.body-part-button').forEach(button => {
button.addEventListener('click', () => {
const bodyPart = button.dataset.bodyPart;
switchBodyPart(bodyPart);
});
});
// 切换身体部位
function switchBodyPart(bodyPart) {
// 腰部(waist)、肩膀(shoulder)和背部(back)都使用相同的图片和点位
if (bodyPart === 'waist' || bodyPart === 'shoulder') {
bodyPart = 'back';
}
if (bodyPart === currentBodyPart) return;
currentBodyPart = bodyPart;
// 更新UI
document.querySelectorAll('.body-part-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.bodyPart === bodyPart);
});
// 清除已选择的点
clearPoints();
// 发送身体部位变更消息
channel.postMessage({
type: 'change_body_part',
body_part: bodyPart
});
}
// 初始化按钮事件
document.querySelectorAll('.mode-button').forEach(button => {
button.addEventListener('click', () => {
const mode = button.dataset.mode;
setMode(mode);
// 显示/隐藏椭圆控制面板
document.getElementById('ellipseControls').classList.toggle('active', mode === 'ellipse');
document.getElementById('lemniscateControls').classList.toggle('active', mode === 'lemniscate');
document.getElementById('cycloidControls').classList.toggle('active', mode === 'cycloid');
document.getElementById('inSpiralControls').classList.toggle('active', mode === 'in_spiral');
document.getElementById('outSpiralControls').classList.toggle('active', mode === 'out_spiral');
// 发送模式变更消息
channel.postMessage({
type: 'mode_change',
mode: mode
});
});
});
// 初始化椭圆参数控制
document.getElementById('widthSlider').addEventListener('input', (e) => {
ellipseParams.width = parseInt(e.target.value);
document.getElementById('widthValue').textContent = ellipseParams.width;
updateEllipse();
});
document.getElementById('cyclesSlider').addEventListener('input', (e) => {
ellipseParams.cycles = parseInt(e.target.value);
document.getElementById('cyclesValue').textContent = ellipseParams.cycles;
updateEllipse();
});
document.getElementById('ellipseControls').querySelectorAll('.direction-button').forEach(button => {
button.addEventListener('click', () => {
const direction = button.dataset.direction;
ellipseParams.direction = direction;
document.getElementById('ellipseControls').querySelectorAll('.direction-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.direction === direction);
});
updateEllipse();
});
});
// 初始化8字形参数控制
document.getElementById('lemniscateWidthSlider').addEventListener('input', (e) => {
lemniscateParams.width = parseInt(e.target.value);
document.getElementById('lemniscateWidthValue').textContent = lemniscateParams.width;
updateLemniscate();
});
document.getElementById('lemniscateCyclesSlider').addEventListener('input', (e) => {
lemniscateParams.cycles = parseInt(e.target.value);
document.getElementById('lemniscateCyclesValue').textContent = lemniscateParams.cycles;
updateLemniscate();
});
document.getElementById('lemniscateControls').querySelectorAll('.direction-button').forEach(button => {
button.addEventListener('click', () => {
const direction = button.dataset.direction;
lemniscateParams.direction = direction;
document.getElementById('lemniscateControls').querySelectorAll('.direction-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.direction === direction);
});
updateLemniscate();
});
});
// 初始化摆线参数控制
document.getElementById('cycloidWidthSlider').addEventListener('input', (e) => {
cycloidParams.width = parseInt(e.target.value);
document.getElementById('cycloidWidthValue').textContent = cycloidParams.width;
updateCycloid();
});
document.getElementById('cycloidCyclesSlider').addEventListener('input', (e) => {
cycloidParams.cycles = parseInt(e.target.value);
document.getElementById('cycloidCyclesValue').textContent = cycloidParams.cycles;
updateCycloid();
});
document.getElementById('cycloidControls').querySelectorAll('.direction-button').forEach(button => {
button.addEventListener('click', () => {
const direction = button.dataset.direction;
cycloidParams.direction = direction;
document.getElementById('cycloidControls').querySelectorAll('.direction-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.direction === direction);
});
updateCycloid();
});
});
// 初始化内螺旋参数控制
document.getElementById('inSpiralWidthSlider').addEventListener('input', (e) => {
inSpiralParams.width = parseInt(e.target.value);
document.getElementById('inSpiralWidthValue').textContent = inSpiralParams.width;
updateInSpiral();
});
document.getElementById('inSpiralCyclesSlider').addEventListener('input', (e) => {
inSpiralParams.cycles = parseInt(e.target.value);
document.getElementById('inSpiralCyclesValue').textContent = inSpiralParams.cycles;
updateInSpiral();
});
document.getElementById('inSpiralControls').querySelectorAll('.direction-button').forEach(button => {
button.addEventListener('click', () => {
const direction = button.dataset.direction;
inSpiralParams.direction = direction;
document.getElementById('inSpiralControls').querySelectorAll('.direction-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.direction === direction);
});
updateInSpiral();
});
});
// 初始化外螺旋参数控制
document.getElementById('outSpiralWidthSlider').addEventListener('input', (e) => {
outSpiralParams.width = parseInt(e.target.value);
document.getElementById('outSpiralWidthValue').textContent = outSpiralParams.width;
updateOutSpiral();
});
document.getElementById('outSpiralCyclesSlider').addEventListener('input', (e) => {
outSpiralParams.cycles = parseInt(e.target.value);
document.getElementById('outSpiralCyclesValue').textContent = outSpiralParams.cycles;
updateOutSpiral();
});
document.getElementById('outSpiralControls').querySelectorAll('.direction-button').forEach(button => {
button.addEventListener('click', () => {
const direction = button.dataset.direction;
outSpiralParams.direction = direction;
document.getElementById('outSpiralControls').querySelectorAll('.direction-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.direction === direction);
});
updateOutSpiral();
});
});
// 设置模式
function setMode(mode) {
currentMode = mode;
document.querySelectorAll('.mode-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.mode === mode);
});
// 清除已选择的点
clearPoints();
}
// 清除选择的点
function clearPoints() {
selectedPoints = [];
document.getElementById('point1').textContent = '第一个点:未选择';
document.getElementById('point2').textContent = '第二个点:未选择';
// 发送清除点的消息
channel.postMessage({
type: 'clear_points'
});
}
// 更新椭圆
function updateEllipse() {
if (selectedPoints.length === 2) {
channel.postMessage({
type: 'draw_shape',
mode: 'ellipse',
points: selectedPoints,
params: ellipseParams
});
}
}
// 更新8字形
function updateLemniscate() {
if (selectedPoints.length === 2) {
channel.postMessage({
type: 'draw_shape',
mode: 'lemniscate',
points: selectedPoints,
params: lemniscateParams
});
}
}
// 更新摆线
function updateCycloid() {
if (selectedPoints.length === 2) {
channel.postMessage({
type: 'draw_shape',
mode: 'cycloid',
points: selectedPoints,
params: cycloidParams
});
}
}
// 更新内螺旋
function updateInSpiral() {
if (selectedPoints.length === 2) {
channel.postMessage({
type: 'draw_shape',
mode: 'in_spiral',
points: selectedPoints,
params: inSpiralParams
});
}
}
// 更新外螺旋
function updateOutSpiral() {
if (selectedPoints.length === 2) {
channel.postMessage({
type: 'draw_shape',
mode: 'out_spiral',
points: selectedPoints,
params: outSpiralParams
});
}
}
// 监听清除按钮
document.getElementById('clearPoints').addEventListener('click', clearPoints);
// 监听来自可视化页面的消息
channel.onmessage = (event) => {
const { type, data } = event.data;
if (type === 'point_selected') {
if (currentMode === 'none') return;
// 如果已经有两个点,清空选择
if (selectedPoints.length >= 2) {
selectedPoints = [];
document.getElementById('point1').textContent = '第一个点:未选择';
document.getElementById('point2').textContent = '第二个点:未选择';
}
// 添加新的点
selectedPoints.push(data);
const pointIndex = selectedPoints.length;
document.getElementById(`point${pointIndex}`).textContent =
`${pointIndex}个点:${data.name} (${Math.round(data.x)}, ${Math.round(data.y)})`;
// 如果已选择两个点,发送绘制消息
if (selectedPoints.length === 2) {
channel.postMessage({
type: 'draw_shape',
mode: currentMode,
points: selectedPoints,
params: currentMode === 'ellipse' ? ellipseParams :
currentMode === 'lemniscate' ? lemniscateParams :
currentMode === 'cycloid' ? cycloidParams :
currentMode === 'in_spiral' ? inSpiralParams :
currentMode === 'out_spiral' ? outSpiralParams : undefined
});
}
}
};
</script>
</body>
</html>