| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- import React, { useEffect, useState } from 'react';
- import { useDispatch, useSelector } from 'react-redux';
- import type { AppDispatch, RootState } from '@/states/store';
- import { fetchViewsOrProtocols } from '@/states/patient/viewSelection';
- import { setCurrentBodyPart } from '@/states/bodyPartSlice';
- // ✅ 独立 SVG 文件(不内联)
- // import { ReactComponent as HumanSvg } from '@/assets/imgs/VirtualHuman/human.svg';
- import HumanSvg from '@/assets/imgs/VirtualHuman/human.svg';
- interface HumanBodySvgProps {
- // 容器样式(覆盖默认的 .human-container)
- containerStyle?: React.CSSProperties;
- // SVG 样式(覆盖默认的 .human-svg)
- svgStyle?: React.CSSProperties;
- // 自定义类名(追加到默认类名)
- className?: string;
- // 完全自定义的内联样式
- style?: React.CSSProperties;
- }
- const HumanBody: React.FC<HumanBodySvgProps> = ({
- containerStyle,
- svgStyle,
- className,
- style
- }) => {
- const [activePart, setActivePart] = useState<string | null>(null);
- const dispatch = useDispatch<AppDispatch>();
- const currentPatientType = useSelector(
- (state: RootState) => state.patientType.current
- );
- const bodyPartsState = useSelector(
- (state: RootState) => state.bodyPart.byPatientType
- );
- const selected = useSelector(
- (state: RootState) => state.selection.selected
- );
- /**
- * SVG 统一点击入口(事件委托)
- */
- const handleSvgClick = (e: React.MouseEvent<SVGSVGElement>) => {
- const target = e.target as Element;
- let serverName;
- // 情况 1:点在 image 上(少数浏览器)
- if (target.tagName === 'image') {
- console.log((target as SVGImageElement).dataset.part);
- serverName= (target as SVGImageElement).dataset.part;
- }
- // 情况 2:点在 g 上
- if (target.tagName === 'g') {
- const image = target.querySelector('image[data-part]');
- if (image) {
- console.log((image as SVGImageElement).dataset.part);
- serverName= (image as SVGImageElement).dataset.part;
- }
- }
- if (!serverName) return;
- console.log('点击身体部位:', serverName);
- setActivePart(serverName);
- const selectedBodyPart =
- bodyPartsState.find(
- (item) => item.body_part_id === serverName
- ) || null;
- dispatch(setCurrentBodyPart(selectedBodyPart));
- dispatch(
- fetchViewsOrProtocols({
- selection: selected,
- patientType: currentPatientType?.patient_type_id ?? null,
- bodyPart: serverName,
- })
- );
- };
- /**
- * 控制 SVG 高亮状态
- * 不修改 SVG 文件本身
- */
- useEffect(() => {
- const svg = document.querySelector(
- '.human-svg'
- ) as SVGSVGElement | null;
- if (!svg) return;
- // 清空所有状态
- svg.querySelectorAll('[id]').forEach((el) => {
- el.classList.remove('active');
- el.classList.add('inactive');
- });
- if (activePart) {
- const activeEl = svg.querySelector(
- `#${CSS.escape(activePart)}`
- );
- activeEl?.classList.remove('inactive');
- activeEl?.classList.add('active');
- }
- }, [activePart]);
- return (
- <>
- {/* ===== 样式(集中在一个文件内)===== */}
- <style>
- {`
- .human-container {
- width: 294px;
- // height: 594px;
- user-select: none;
- }
- .human-svg {
- width: 100%;
- height: 100%;
- opacity: 0.95;
- }
- .human-svg g[data-part] {
- pointer-events: bounding-box;
- cursor: pointer;
- //transition: opacity 0.2s ease, filter 0.2s ease;
- outline: 1px solid red;
- }
- /* 默认状态 */
- // .human-svg image[data-part] {
- // cursor: pointer;
- // pointer-events: all;
- // // opacity: 0.35;
- // transition: opacity 0.2s ease, filter 0.2s ease;
- // outline: 1px solid red;
- // }
- /* hover */
- .human-svg [data-part]:hover {
- //opacity: 0.2;
- filter: drop-shadow(0 0 16px red);
- }
- /* 激活状态 */
- .human-svg .active {
- opacity: 1;
- filter: drop-shadow(0 0 6px #409eff);
- }
- /* 非激活 */
- .human-svg .inactive {
- opacity: 0.25;
- }
- `}
- </style>
- {/* ===== SVG 容器 ===== */}
- <div className="human-container" style={containerStyle}>
- <HumanSvg
- className="human-svg"
- style={svgStyle}
- onMouseDown={handleSvgClick}
- />
- </div>
- </>
- );
- }
- export default HumanBody;
|