Files
dsProject/dsLightRag/static/XingJun/js/script.js
2025-09-10 08:32:07 +08:00

560 lines
22 KiB
JavaScript
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.

// 添加常量定义
const ARROW_WIDTH = 155;
const ARROW_HEIGHT = 173;
const STOP_DISTANCE = 50;
// 添加全局变量声明
let centerDot = null;
document.addEventListener('DOMContentLoaded', function() {
const backgroundContainer = document.querySelector('.background-container');
const imageButtons = document.querySelectorAll('.image-btn');
const markCenterBtn = document.getElementById('markCenterBtn');
const startAnimationBtn = document.getElementById('startAnimationBtn');
const resetBtn = document.getElementById('resetBtn');
const contextMenu = document.getElementById('contextMenu');
const deleteArrow = document.getElementById('deleteArrow');
let selectedArrow = null;
centerDot = null; // 修改为直接赋值
// 添加重置按钮事件监听器
resetBtn.addEventListener('click', function() {
location.reload();
});
// 添加开始按钮事件监听器
startAnimationBtn.addEventListener('click', startAnimation);
// 动画控制函数
function startAnimation() {
if (!centerDot) {
alert('请先确定中心点');
return;
}
const centerRect = centerDot.getBoundingClientRect();
const containerRect = backgroundContainer.getBoundingClientRect();
const centerX = centerRect.left - containerRect.left;
const centerY = centerRect.top - containerRect.top;
const arrows = document.querySelectorAll('.arrow-container');
if (arrows.length === 0) {
alert('没有可移动的箭头');
return;
}
arrows.forEach(arrow => {
animateArrow(arrow, centerX, centerY);
});
}
// 单个箭头动画函数
function animateArrow(arrow, targetX, targetY) {
const arrowWidth = ARROW_WIDTH;
const arrowHeight = ARROW_HEIGHT;
const centerOffsetX = arrowWidth / 2;
const centerOffsetY = arrowHeight / 2;
// 使用当前箭头的停止距离默认为50
const stopDistance = parseInt(arrow.dataset.stopDistance) || 50;
const rect = arrow.getBoundingClientRect();
const containerRect = backgroundContainer.getBoundingClientRect();
let currentX = rect.left - containerRect.left + centerOffsetX;
let currentY = rect.top - containerRect.top + centerOffsetY;
const offsetRange = 20;
const randomOffsetX = (Math.random() - 0.5) * offsetRange;
const randomOffsetY = (Math.random() - 0.5) * offsetRange;
const finalTargetX = targetX + randomOffsetX;
const finalTargetY = targetY + randomOffsetY;
const dxTotal = finalTargetX - currentX;
const dyTotal = finalTargetY - currentY;
const distanceTotal = Math.sqrt(dxTotal * dxTotal + dyTotal * dyTotal);
let adjustedTargetX, adjustedTargetY;
if (distanceTotal > stopDistance) {
const ratio = (distanceTotal - stopDistance) / distanceTotal;
adjustedTargetX = currentX + dxTotal * ratio;
adjustedTargetY = currentY + dyTotal * ratio;
} else {
adjustedTargetX = currentX;
adjustedTargetY = currentY;
}
function updatePosition() {
const dx = adjustedTargetX - currentX;
const dy = adjustedTargetY - currentY;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 2) {
arrow.style.left = (adjustedTargetX - centerOffsetX) + 'px';
arrow.style.top = (adjustedTargetY - centerOffsetY) + 'px';
return;
}
const step = Math.max(distance * 0.05, 1);
const moveX = (dx / distance) * step;
const moveY = (dy / distance) * step;
currentX += moveX;
currentY += moveY;
arrow.style.left = (currentX - centerOffsetX) + 'px';
arrow.style.top = (currentY - centerOffsetY) + 'px';
requestAnimationFrame(updatePosition);
}
updatePosition();
}
// 加载保存的元素
loadAllElements();
// 绑定箭头按钮事件
imageButtons.forEach(button => {
button.addEventListener('click', () => {
const imagePath = button.getAttribute('data-image');
addImageToContainer(imagePath);
});
});
// 右键菜单事件
deleteArrow.addEventListener('click', function() {
if (selectedArrow) {
selectedArrow.remove();
saveAllElements();
contextMenu.style.display = 'none';
selectedArrow = null;
}
});
// 为匹配角菜单项添加事件监听器
document.querySelectorAll('.menu-item').forEach(item => {
item.addEventListener('click', function() {
if (selectedArrow) {
const referencePoint = this.getAttribute('data-point');
selectedArrow.dataset.referencePoint = referencePoint;
saveAllElements();
contextMenu.style.display = 'none';
selectedArrow = null;
}
});
});
// 点击空白处关闭菜单
document.addEventListener('click', function() {
contextMenu.style.display = 'none';
selectedArrow = null;
});
// 标记中心点功能
markCenterBtn.addEventListener('click', function() {
if (centerDot) {
centerDot.remove();
centerDot = null;
}
function handleMapClick(e) {
const rect = backgroundContainer.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
centerDot = document.createElement('div');
centerDot.className = 'center-dot';
centerDot.style.left = x + 'px';
centerDot.style.top = y + 'px';
backgroundContainer.appendChild(centerDot);
backgroundContainer.removeEventListener('click', handleMapClick);
console.log('[中心点] 已标记中心点:', { left: centerDot.style.left, top: centerDot.style.top });
saveAllElements();
}
backgroundContainer.addEventListener('click', handleMapClick);
});
// 添加箭头函数
function addImageToContainer(imagePath, position = null, textContent = '') {
const container = document.createElement('div');
container.className = 'arrow-container';
container.dataset.imagePath = imagePath;
const newImage = document.createElement('div');
newImage.className = 'arrow-image';
newImage.style.backgroundImage = `url('${imagePath}')`;
container.appendChild(newImage);
// 添加文字元素
if (textContent) {
const textElement = document.createElement('div');
textElement.className = 'arrow-text';
textElement.textContent = textContent;
container.dataset.text = textContent;
container.appendChild(textElement);
}
// 设置位置
const arrowWidth = ARROW_WIDTH;
const arrowHeight = ARROW_HEIGHT;
const centerOffsetX = arrowWidth / 2;
const centerOffsetY = arrowHeight / 2;
if (position) {
container.style.left = parseFloat(position.left) + 'px';
container.style.top = parseFloat(position.top) + 'px';
if (position.referencePoint) {
container.dataset.referencePoint = position.referencePoint;
}
if (position.stopDistance) {
container.dataset.stopDistance = position.stopDistance;
}
} else {
const x = Math.random() * (backgroundContainer.offsetWidth - arrowWidth) + centerOffsetX;
const y = Math.random() * (backgroundContainer.offsetHeight - arrowHeight) + centerOffsetY;
container.style.left = (x - centerOffsetX) + 'px';
container.style.top = (y - centerOffsetY) + 'px';
}
// 双击编辑文字和停止距离
container.addEventListener('dblclick', (e) => {
e.stopPropagation();
const currentText = container.dataset.text || '';
const currentStopDistance = container.dataset.stopDistance || '50';
// 创建自定义弹窗内容使用layui表单样式
const content = `
<div style="padding: 20px;">
<div class="layui-form-item">
<label class="layui-form-label">文字内容:</label>
<div class="layui-input-block">
<input type="text" id="arrowText" value="${currentText}" class="layui-input" style="width: 100%;">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">停止距离 (px):</label>
<div class="layui-input-block">
<input type="number" id="arrowStopDistance" value="${currentStopDistance}" min="0" max="200" class="layui-input" style="width: 100%;">
</div>
</div>
</div>
`;
// 使用layer.open创建自定义弹窗增加宽度并应用layui样式
layer.open({
type: 1,
title: '编辑箭头属性',
area: ['500px', 'auto'], // 增加宽度到500px
content: content,
btn: ['确定', '取消'],
skin: 'layui-layer-molv', // 使用layui默认皮肤
success: function(layero) {
// 初始化layui表单样式
layui.form.render(null, 'arrowForm');
},
yes: function(index, layero) {
const textValue = layero.find('#arrowText').val().trim();
const stopDistanceValue = layero.find('#arrowStopDistance').val();
// 保存文字内容
if (textValue === '') {
container.querySelector('.arrow-text')?.remove();
delete container.dataset.text;
} else {
let textElement = container.querySelector('.arrow-text');
if (!textElement) {
textElement = document.createElement('div');
textElement.className = 'arrow-text';
container.appendChild(textElement);
}
textElement.textContent = textValue;
container.dataset.text = textValue;
}
// 保存停止距离
if (!isNaN(stopDistanceValue) && stopDistanceValue >= 0) {
container.dataset.stopDistance = stopDistanceValue;
}
saveAllElements();
layer.close(index);
}
});
});
// 右键菜单支持
container.addEventListener('contextmenu', function(e) {
e.preventDefault();
selectedArrow = this;
contextMenu.style.left = `${e.clientX}px`;
contextMenu.style.top = `${e.clientY}px`;
contextMenu.style.display = 'block';
const currentPoint = selectedArrow.dataset.referencePoint || 'top-left';
document.querySelectorAll('.menu-item').forEach(item => {
const checkMark = item.querySelector('.check-mark');
if (!checkMark) return;
checkMark.textContent = item.getAttribute('data-point') === currentPoint ? '✓' : '';
});
});
// 拖拽功能
let isDragging = false;
let offsetX, offsetY;
newImage.addEventListener('mousedown', startDrag);
function startDrag(e) {
e.preventDefault();
isDragging = true;
selectedArrow = container;
container.style.zIndex = '10';
const rect = container.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDrag);
}
function drag(e) {
if (!isDragging) return;
const containerRect = backgroundContainer.getBoundingClientRect();
const x = e.clientX - containerRect.left - offsetX;
const y = e.clientY - containerRect.top - offsetY;
container.style.left = x + 'px';
container.style.top = y + 'px';
}
function stopDrag() {
if (isDragging) {
isDragging = false;
container.style.zIndex = '1';
saveAllElements();
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', stopDrag);
}
}
backgroundContainer.appendChild(container);
saveAllElements();
return container;
}
// 保存所有元素状态
function saveAllElements() {
const elements = [];
// 保存中心点
if (centerDot) {
localStorage.setItem('centerPoint', JSON.stringify({
left: centerDot.style.left,
top: centerDot.style.top
}));
} else {
localStorage.removeItem('centerPoint');
}
// 删除全局停止距离的保存
localStorage.removeItem('globalStopDistance');
// 保存箭头(添加stopDistance属性)
document.querySelectorAll('.arrow-container').forEach(arrow => {
elements.push({
type: 'arrow',
imagePath: arrow.dataset.imagePath,
left: arrow.style.left,
top: arrow.style.top,
text: arrow.dataset.text || '',
referencePoint: arrow.dataset.referencePoint || 'top-left',
stopDistance: arrow.dataset.stopDistance || '50' // 添加停止距离保存
});
});
localStorage.setItem('savedElements', JSON.stringify(elements));
console.log('[保存] 已保存元素:', elements);
}
// 加载所有元素状态
function loadAllElements() {
// 加载中心点
const savedCenter = localStorage.getItem('centerPoint');
if (savedCenter) {
const center = JSON.parse(savedCenter);
centerDot = document.createElement('div');
centerDot.className = 'center-dot';
centerDot.style.left = center.left;
centerDot.style.top = center.top;
backgroundContainer.appendChild(centerDot);
}
// 加载箭头
const savedElements = localStorage.getItem('savedElements');
if (savedElements) {
const elements = JSON.parse(savedElements);
elements.forEach(element => {
if (element.type === 'arrow') {
// Fix image path by adding 'images/' prefix if missing
if (!element.imagePath.startsWith('images/')) {
element.imagePath = `images/${element.imagePath}`;
}
addImageToContainer(element.imagePath, element, element.text);
}
});
console.log('[加载] 已加载元素:', elements);
}
}
// 移除内部DOMContentLoaded嵌套直接绑定事件
// 阻止右键菜单冒泡
contextMenu.addEventListener('click', function(e) {
e.stopPropagation();
});
// 导入数据按钮事件 - 修复DOMContentLoaded嵌套问题
const importDataBtn = document.getElementById('importDataBtn');
if (!importDataBtn) {
//alert('错误:未找到导入按钮元素!');
console.error('导入按钮元素不存在');
} else {
//alert('导入按钮已找到,准备绑定事件...'); // 调试提示
importDataBtn.addEventListener('click', function() {
//alert('导入按钮被点击,开始加载数据...'); // 确认进入点击事件
console.log('导入按钮点击事件触发');
// 修正JSON文件路径根据HTTP服务器根目录调整
const jsonFilePath = './json/arrow_data_2025-09-10.json';
const loadIndex = layer.load(2, {shade: [0.3, '#333']});
// 添加完整的错误追踪
console.log('尝试加载JSON文件:', jsonFilePath);
fetch(jsonFilePath)
.then(response => {
console.log('服务器响应状态:', response.status);
//alert(`服务器响应状态: ${response.status}`);
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
return response.json();
})
.then(data => {
console.log('JSON解析成功:', data);
//alert('数据解析成功,准备保存...');
// 保存数据到localStorage
localStorage.setItem('savedElements', JSON.stringify(data.elements || []));
localStorage.setItem('centerPoint', JSON.stringify(data.centerPoint || {}));
layer.close(loadIndex);
//alert('导入成功,即将刷新页面');
location.reload();
})
.catch(error => {
console.error('完整错误信息:', error);
layer.close(loadIndex);
alert(`导入失败: ${error.message}\n请查看控制台获取详细信息`);
});
});
}
});
// 保存数据到JSON文件并下载
function saveDataToFile() {
// 获取所有箭头元素数据 - 完全匹配localStorage格式
const arrows = document.querySelectorAll('.arrow-container');
const elements = Array.from(arrows).map(arrow => ({
type: 'arrow', // 添加缺失的type字段
imagePath: arrow.dataset.imagePath,
left: arrow.style.left, // 保留px单位和字符串类型
top: arrow.style.top, // 保留px单位和字符串类型
text: arrow.dataset.text || '',
referencePoint: arrow.dataset.referencePoint || 'top-left',
stopDistance: arrow.dataset.stopDistance || '50' // 保留字符串类型
}));
// 获取中心点数据 - 保留px单位和字符串类型
const centerPoint = centerDot ? {
left: centerDot.style.left,
top: centerDot.style.top
} : null;
// 创建完整的导出数据对象
const exportData = {
elements: elements,
centerPoint: centerPoint,
exportTime: new Date().toISOString()
};
try {
const jsonData = JSON.stringify(exportData, null, 2);
const blob = new Blob([jsonData], { type: 'application/json;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
const dateStr = new Date().toISOString().slice(0, 10);
a.download = `arrow_data_${dateStr}.json`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 0);
console.log('数据已成功导出');
layer.msg('数据导出成功', {icon: 1});
} catch (error) {
console.error('导出数据失败:', error);
layer.msg('导出失败: ' + error.message, {icon: 2});
}
}
// 为保存数据按钮添加点击事件
document.getElementById('saveDataBtn').addEventListener('click', saveDataToFile);
// 清空数据功能实现
function clearAllData() {
// 显示确认对话框
layer.confirm('确定要清空所有数据吗?此操作不可恢复!', {
icon: 3,
title: '警告',
btn: ['确认清空', '取消']
}, function(index) {
// 确认清空数据
try {
// 1. 清除localStorage数据
localStorage.removeItem('savedElements');
localStorage.removeItem('centerPoint');
// 2. 清除DOM中的箭头元素
document.querySelectorAll('.arrow-container').forEach(arrow => {
arrow.remove();
});
// 3. 清除中心点
if (centerDot) {
centerDot.remove();
centerDot = null;
}
// 4. 刷新页面或更新UI
layer.msg('所有数据已成功清空', {icon: 1, time: 1500}, function() {
location.reload(); // 简单粗暴的刷新确保UI完全重置
});
} catch (error) {
console.error('清空数据失败:', error);
layer.msg('清空失败: ' + error.message, {icon: 2});
}
layer.close(index);
});
}
// 为清空数据按钮添加点击事件
document.addEventListener('DOMContentLoaded', function() {
const clearDataBtn = document.getElementById('clearDataBtn');
if (clearDataBtn) {
clearDataBtn.addEventListener('click', clearAllData);
}
});