0.5.0.2
This commit is contained in:
@@ -14,6 +14,7 @@ import { useFamily } from "@/context/family-context"
|
||||
declare global {
|
||||
interface Window {
|
||||
d3ChartContextMenu?: (event: MouseEvent, memberId: string) => void
|
||||
d3ChartSpouseClick?: (event: Event, spouseId: string) => void
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,6 +347,7 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
|
||||
|
||||
// 检查是否被选中
|
||||
const isSelected = selectedMembers.includes(node.id)
|
||||
const isSpouseSelected = node.spouseId && selectedMembers.includes(node.spouseId)
|
||||
|
||||
// 动态宽度:有配偶时加宽,与 OrgChart 配置保持一致
|
||||
const width = hasSpouse ? 260 : 160
|
||||
@@ -455,8 +457,8 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
|
||||
<div
|
||||
class="spouse-area"
|
||||
data-spouse-id="${node.spouseId}"
|
||||
style="display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 1; cursor: pointer;"
|
||||
onclick="event.stopPropagation()"
|
||||
style="display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 1; cursor: pointer; position: relative;"
|
||||
onclick="event.stopPropagation(); window.d3ChartSpouseClick && window.d3ChartSpouseClick(event, '${node.spouseId}')"
|
||||
>
|
||||
<div style="
|
||||
width: 50px;
|
||||
@@ -521,41 +523,34 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
|
||||
.render()
|
||||
.expandAll() // 初始化时展开所有节点
|
||||
|
||||
// 添加配偶区域点击事件监听
|
||||
setTimeout(() => {
|
||||
const spouseAreas = chartRef.current?.querySelectorAll('.spouse-area')
|
||||
spouseAreas?.forEach((area) => {
|
||||
area.addEventListener('click', (e) => {
|
||||
e.stopPropagation() // 阻止事件冒泡到节点
|
||||
const spouseId = (area as HTMLElement).getAttribute('data-spouse-id')
|
||||
if (spouseId && onMemberClick) {
|
||||
onMemberClick(spouseId)
|
||||
}
|
||||
})
|
||||
})
|
||||
// 注册全局配偶点击处理函数
|
||||
window.d3ChartSpouseClick = (event: Event, spouseId: string) => {
|
||||
event.stopPropagation()
|
||||
if (spouseId && onMemberClick) {
|
||||
onMemberClick(spouseId)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加右键菜单支持
|
||||
// 直接为节点添加右键事件监听
|
||||
setTimeout(() => {
|
||||
const nodes = chartRef.current?.querySelectorAll('[data-member-id]')
|
||||
console.log('Found nodes for context menu:', nodes?.length)
|
||||
nodes?.forEach((node) => {
|
||||
const memberId = (node as HTMLElement).getAttribute('data-member-id')
|
||||
if (memberId) {
|
||||
node.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault()
|
||||
const mouseEvent = e as MouseEvent
|
||||
const member = members[memberId]
|
||||
if (member && !relationMode) {
|
||||
console.log('Right click on member:', member.fullName, 'at', mouseEvent.clientX, mouseEvent.clientY)
|
||||
setContextMenuNode(member)
|
||||
setContextMenuPosition({ x: mouseEvent.clientX, y: mouseEvent.clientY })
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}, 200)
|
||||
}, 100)
|
||||
// 添加右键菜单支持
|
||||
setTimeout(() => {
|
||||
const nodes = chartRef.current?.querySelectorAll('[data-member-id]')
|
||||
console.log('Found nodes for context menu:', nodes?.length)
|
||||
nodes?.forEach((node) => {
|
||||
const memberId = (node as HTMLElement).getAttribute('data-member-id')
|
||||
if (memberId) {
|
||||
node.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault()
|
||||
const mouseEvent = e as MouseEvent
|
||||
const member = members[memberId]
|
||||
if (member && !relationMode) {
|
||||
console.log('Right click on member:', member.fullName, 'at', mouseEvent.clientX, mouseEvent.clientY)
|
||||
setContextMenuNode(member)
|
||||
setContextMenuPosition({ x: mouseEvent.clientX, y: mouseEvent.clientY })
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}, 200)
|
||||
|
||||
chartInstanceRef.current = chart
|
||||
setIsReady(true)
|
||||
@@ -569,6 +564,9 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
|
||||
if (window.d3ChartContextMenu) {
|
||||
delete window.d3ChartContextMenu
|
||||
}
|
||||
if (window.d3ChartSpouseClick) {
|
||||
delete window.d3ChartSpouseClick
|
||||
}
|
||||
}
|
||||
}, [members, rootId, onMemberClick, avatarCache])
|
||||
|
||||
@@ -581,28 +579,30 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
|
||||
const existingMarks = chartRef.current.querySelectorAll('.selection-mark')
|
||||
existingMarks.forEach(mark => mark.remove())
|
||||
|
||||
// 重置所有节点的边框样式
|
||||
const allNodes = chartRef.current.querySelectorAll('[data-member-id]')
|
||||
allNodes.forEach(node => {
|
||||
;(node as HTMLElement).style.boxShadow = '0 4px 12px rgba(0,0,0,0.12)'
|
||||
})
|
||||
|
||||
// 为选中的成员添加选中标记
|
||||
// 为选中的成员添加选中标记(只打钩,不改变边框和背景)
|
||||
selectedMembers.forEach(memberId => {
|
||||
console.log('Marking member as selected:', memberId)
|
||||
|
||||
// 先尝试查找主成员节点
|
||||
const nodeElement = chartRef.current?.querySelector(`[data-member-id="${memberId}"]`)
|
||||
console.log('Found node element:', nodeElement)
|
||||
|
||||
// 再尝试查找配偶区域
|
||||
const spouseElement = chartRef.current?.querySelector(`[data-spouse-id="${memberId}"]`)
|
||||
|
||||
if (nodeElement) {
|
||||
// 添加选中边框效果
|
||||
;(nodeElement as HTMLElement).style.border = '3px solid #2563eb'
|
||||
;(nodeElement as HTMLElement).style.boxShadow = '0 0 0 3px rgba(37, 99, 235, 0.3), 0 4px 12px rgba(0,0,0,0.12)'
|
||||
|
||||
// 添加打钩标记
|
||||
// 主成员:只添加打钩标记(左边)
|
||||
const checkMark = document.createElement('div')
|
||||
checkMark.className = 'selection-mark'
|
||||
checkMark.style.cssText = 'position: absolute; top: 6px; right: 6px; z-index: 20;'
|
||||
checkMark.style.cssText = 'position: absolute; top: 6px; left: 6px; z-index: 20;'
|
||||
checkMark.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="#2563eb" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="9 12 11 14 15 10" fill="none" stroke="white" stroke-width="2.5"/></svg>`
|
||||
nodeElement.appendChild(checkMark)
|
||||
} else if (spouseElement) {
|
||||
// 配偶:只添加打钩标记
|
||||
const checkMark = document.createElement('div')
|
||||
checkMark.className = 'selection-mark'
|
||||
checkMark.style.cssText = 'position: absolute; top: -5px; right: -5px; z-index: 20;'
|
||||
checkMark.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="#2563eb" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="9 12 11 14 15 10" fill="none" stroke="white" stroke-width="2.5"/></svg>`
|
||||
spouseElement.appendChild(checkMark)
|
||||
}
|
||||
})
|
||||
}, [selectedMembers, isReady])
|
||||
|
||||
@@ -192,9 +192,7 @@ function PersonCard({
|
||||
"border-[3px] rounded-none",
|
||||
"shadow-lg hover:shadow-xl",
|
||||
// 根据状态设置样式
|
||||
isSelected
|
||||
? "border-blue-600 bg-blue-50 dark:bg-blue-950/50 ring-2 ring-blue-400"
|
||||
: isHighlighted
|
||||
isHighlighted
|
||||
? "border-yellow-600 bg-yellow-50/90 dark:bg-yellow-950/50 ring-2 ring-yellow-400 animate-pulse"
|
||||
: isFounder || isRoot
|
||||
? "border-amber-500 bg-gradient-to-b from-amber-100 via-yellow-50 to-amber-100 dark:from-amber-900/40 dark:to-yellow-900/20 shadow-xl ring-1 ring-amber-300"
|
||||
@@ -205,15 +203,14 @@ function PersonCard({
|
||||
: isMale
|
||||
? "border-amber-800 bg-gradient-to-b from-amber-50 via-orange-50/30 to-amber-50/80 dark:from-amber-950/40 dark:to-orange-950/20"
|
||||
: "border-rose-800 bg-gradient-to-b from-rose-50 via-pink-50/30 to-rose-50/80 dark:from-rose-950/40 dark:to-pink-950/20",
|
||||
relationMode && "hover:border-blue-600 hover:shadow-xl"
|
||||
relationMode && "hover:shadow-xl"
|
||||
)}
|
||||
onClick={() => onSelect?.(member)}
|
||||
>
|
||||
{/* 内边框装饰 */}
|
||||
<div className={cn(
|
||||
"absolute inset-[3px] border pointer-events-none",
|
||||
isSelected ? "border-blue-400/50"
|
||||
: isHighlighted ? "border-yellow-500/50"
|
||||
isHighlighted ? "border-yellow-500/50"
|
||||
: isFounder || isRoot ? "border-amber-400/50"
|
||||
: isSpouse ? "border-stone-400/30"
|
||||
: isDead ? "border-stone-400/40"
|
||||
@@ -224,21 +221,28 @@ function PersonCard({
|
||||
{/* 四角装饰 - 更精致的角花 */}
|
||||
<div className={cn(
|
||||
"absolute top-0 left-0 w-4 h-4 border-t-[3px] border-l-[3px]",
|
||||
isSelected ? "border-blue-600" : isHighlighted ? "border-yellow-600" : isFounder || isRoot ? "border-amber-500" : isSpouse ? "border-stone-500" : isDead ? "border-stone-500" : isMale ? "border-amber-800" : "border-rose-800"
|
||||
isHighlighted ? "border-yellow-600" : isFounder || isRoot ? "border-amber-500" : isSpouse ? "border-stone-500" : isDead ? "border-stone-500" : isMale ? "border-amber-800" : "border-rose-800"
|
||||
)} />
|
||||
<div className={cn(
|
||||
"absolute top-0 right-0 w-4 h-4 border-t-[3px] border-r-[3px]",
|
||||
isSelected ? "border-blue-600" : isHighlighted ? "border-yellow-600" : isFounder || isRoot ? "border-amber-500" : isSpouse ? "border-stone-500" : isDead ? "border-stone-500" : isMale ? "border-amber-800" : "border-rose-800"
|
||||
isHighlighted ? "border-yellow-600" : isFounder || isRoot ? "border-amber-500" : isSpouse ? "border-stone-500" : isDead ? "border-stone-500" : isMale ? "border-amber-800" : "border-rose-800"
|
||||
)} />
|
||||
<div className={cn(
|
||||
"absolute bottom-0 left-0 w-4 h-4 border-b-[3px] border-l-[3px]",
|
||||
isSelected ? "border-blue-600" : isHighlighted ? "border-yellow-600" : isFounder || isRoot ? "border-amber-500" : isSpouse ? "border-stone-500" : isDead ? "border-stone-500" : isMale ? "border-amber-800" : "border-rose-800"
|
||||
isHighlighted ? "border-yellow-600" : isFounder || isRoot ? "border-amber-500" : isSpouse ? "border-stone-500" : isDead ? "border-stone-500" : isMale ? "border-amber-800" : "border-rose-800"
|
||||
)} />
|
||||
<div className={cn(
|
||||
"absolute bottom-0 right-0 w-4 h-4 border-b-[3px] border-r-[3px]",
|
||||
isSelected ? "border-blue-600" : isHighlighted ? "border-yellow-600" : isFounder || isRoot ? "border-amber-500" : isSpouse ? "border-stone-500" : isDead ? "border-stone-500" : isMale ? "border-amber-800" : "border-rose-800"
|
||||
isHighlighted ? "border-yellow-600" : isFounder || isRoot ? "border-amber-500" : isSpouse ? "border-stone-500" : isDead ? "border-stone-500" : isMale ? "border-amber-800" : "border-rose-800"
|
||||
)} />
|
||||
|
||||
{/* 选中打钩标记 */}
|
||||
{isSelected && (
|
||||
<div className="absolute top-1 left-1 z-20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="#2563eb" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="9 12 11 14 15 10" fill="none" stroke="white" strokeWidth="2.5"/></svg>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 世代标签 - 庄严印章风格 */}
|
||||
<div className={cn(
|
||||
"absolute -top-3 px-3 py-1 text-[11px] font-serif font-bold tracking-wider",
|
||||
|
||||
Reference in New Issue
Block a user