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

530 lines
20 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);
}
}
// 阻止右键菜单冒泡
contextMenu.addEventListener('click', function(e) {
e.stopPropagation();
});
// 替换原导入按钮事件为文件选择模式
document.addEventListener('DOMContentLoaded', function() {
// 导入数据按钮事件 - 全新实现
const importDataBtn = document.getElementById('importDataBtn');
if (importDataBtn) {
importDataBtn.addEventListener('click', function() {
// 创建隐藏的文件选择输入
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json';
fileInput.style.display = 'none';
// 触发文件选择对话框
fileInput.click();
// 文件选择处理
fileInput.onchange = function(e) {
const file = e.target.files[0];
if (!file) return;
console.log('已选择文件:', file.name);
const loadIndex = layer.load(2, {shade: [0.3, '#333']});
const reader = new FileReader();
reader.onload = function(event) {
try {
const data = JSON.parse(event.target.result);
console.log('JSON文件解析成功:', data);
// 保存数据到localStorage
if (data.elements) {
localStorage.setItem('savedElements', JSON.stringify(data.elements));
}
if (data.centerPoint) {
localStorage.setItem('centerPoint', JSON.stringify(data.centerPoint));
}
layer.close(loadIndex);
layer.msg('数据导入成功,页面将刷新', {icon: 1, time: 1500}, function() {
location.reload();
});
} catch (error) {
layer.close(loadIndex);
console.error('JSON解析失败:', error);
layer.msg('文件格式错误请选择有效的JSON文件', {icon: 5});
}
};
reader.onerror = function() {
layer.close(loadIndex);
layer.msg('文件读取失败', {icon: 5});
};
reader.readAsText(file);
};
});
}
});
});
// 保存数据到JSON文件并下载
function saveDataToFile() {
// 获取所有箭头元素数据
const arrows = document.querySelectorAll('.arrow-container');
const elements = Array.from(arrows).map(arrow => ({
imagePath: arrow.dataset.imagePath,
left: arrow.style.left,
top: arrow.style.top,
text: arrow.dataset.text || '',
referencePoint: arrow.dataset.referencePoint || 'top-left',
stopDistance: parseInt(arrow.dataset.stopDistance) || 50
}));
// 获取中心点数据
const centerPoint = centerDot ? {
left: centerDot.style.left,
top: centerDot.style.top
} : null;
// 创建完整的导出数据对象
const exportData = {
elements: elements,
centerPoint: centerPoint,
exportTime: new Date().toISOString()
};
try {
// 生成JSON字符串确保正确处理特殊字符
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('数据已成功导出');
} catch (error) {
console.error('导出数据失败:', error);
layer.msg('导出失败: ' + error.message, {icon: 2});
}
}
// 为保存数据按钮添加点击事件
document.getElementById('saveDataBtn').addEventListener('click', saveDataToFile);