gongxiangp2/components/ShadowSystemIndicator.tsx

108 lines
3.4 KiB
TypeScript

'use client'
import { useState, useEffect, useRef } from 'react'
export default function ShadowSystemIndicator() {
const [isShadowSystem, setIsShadowSystem] = useState(false)
const [isDragging, setIsDragging] = useState(false)
const [position, setPosition] = useState({ x: 0, y: 0 })
const [dragStart, setDragStart] = useState({ x: 0, y: 0 })
const indicatorRef = useRef<HTMLDivElement>(null)
// 检测是否为影子系统
useEffect(() => {
if (typeof window !== 'undefined') {
const isShadow = window.location.port === '5320' || window.location.href.includes(':5320')
setIsShadowSystem(isShadow)
// 从 localStorage 恢复位置
const savedPosition = localStorage.getItem('shadowIndicatorPosition')
if (savedPosition) {
try {
const pos = JSON.parse(savedPosition)
setPosition(pos)
} catch (e) {
// 默认位置:右上角
setPosition({ x: window.innerWidth - 120, y: 80 })
}
} else {
// 默认位置:右上角
setPosition({ x: window.innerWidth - 120, y: 80 })
}
}
}, [])
// 拖拽处理
const handleMouseDown = (e: React.MouseEvent) => {
if (e.button !== 0) return
setIsDragging(true)
setDragStart({
x: e.clientX - position.x,
y: e.clientY - position.y
})
}
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (!isDragging) return
let newX = e.clientX - dragStart.x
let newY = e.clientY - dragStart.y
// 限制在视窗内
if (indicatorRef.current) {
const rect = indicatorRef.current.getBoundingClientRect()
const maxX = window.innerWidth - rect.width
const maxY = window.innerHeight - rect.height
newX = Math.max(0, Math.min(newX, maxX))
newY = Math.max(0, Math.min(newY, maxY))
}
setPosition({ x: newX, y: newY })
}
const handleMouseUp = () => {
if (isDragging) {
// 保存位置到 localStorage
const newPos = { x: position.x, y: position.y }
localStorage.setItem('shadowIndicatorPosition', JSON.stringify(newPos))
}
setIsDragging(false)
}
if (isDragging) {
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
}
return () => {
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}
}, [isDragging, dragStart, position])
if (!isShadowSystem) return null
return (
<div
ref={indicatorRef}
className="fixed z-[100] cursor-move select-none"
style={{
left: `${position.x}px`,
top: `${position.y}px`,
cursor: isDragging ? 'grabbing' : 'grab'
}}
onMouseDown={handleMouseDown}
>
<div className="bg-gradient-to-r from-orange-500 to-red-500 text-white px-3 py-1.5 rounded-lg shadow-lg text-xs font-bold flex items-center gap-1.5">
<svg className="w-3 h-3 animate-pulse" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
<span>影子系统</span>
</div>
</div>
)
}