|
@@ -0,0 +1,266 @@
|
|
|
|
+import React, { useState } from 'react';
|
|
|
|
+import { Layout, Button, Typography, Input, Select, Flex, Divider } from 'antd';
|
|
|
|
+import { ArrowLeftOutlined } from '@ant-design/icons';
|
|
|
|
+import { useDispatch } from 'react-redux';
|
|
|
|
+import { switchToOperationPanel } from '../../../states/panelSwitchSliceForView';
|
|
|
|
+import {
|
|
|
|
+ addCustomMarkToList,
|
|
|
|
+ setSelectedMark,
|
|
|
|
+ setInputText,
|
|
|
|
+ clearInputText,
|
|
|
|
+ selectCustomMarks,
|
|
|
|
+ selectSelectedMark,
|
|
|
|
+ selectInputText,
|
|
|
|
+} from '../../../states/view/markPanelSlice';
|
|
|
|
+import { useAppSelector } from '@/states/store';
|
|
|
|
+import Icon from '@/components/Icon';
|
|
|
|
+import '@/themes/truncateText.css';
|
|
|
|
+
|
|
|
|
+const { Header, Content } = Layout;
|
|
|
|
+const { Title } = Typography;
|
|
|
|
+const { Option } = Select;
|
|
|
|
+
|
|
|
|
+// 预定义标记配置
|
|
|
|
+const PREDEFINED_MARKS = [
|
|
|
|
+ '拉姿', '仰卧', '俯卧', '斜倚',
|
|
|
|
+ '过屈', '过伸', '内旋', '外旋',
|
|
|
|
+ '吸气', '呼气', '负重', '无负重'
|
|
|
|
+];
|
|
|
|
+
|
|
|
|
+// 参考 FunctionArea.tsx 中的 FunctionButton 实现
|
|
|
|
+const MarkButton = ({
|
|
|
|
+ title,
|
|
|
|
+ action,
|
|
|
|
+ iconName,
|
|
|
|
+ onClick,
|
|
|
|
+}: {
|
|
|
|
+ title: string;
|
|
|
|
+ action: string;
|
|
|
|
+ iconName?: string;
|
|
|
|
+ onClick: () => void;
|
|
|
|
+}) => {
|
|
|
|
+ const themeType = useAppSelector((state) => state.theme.themeType);
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <Button
|
|
|
|
+ onClick={onClick}
|
|
|
|
+ icon={
|
|
|
|
+ iconName ? (
|
|
|
|
+ <Icon
|
|
|
|
+ module="module-process"
|
|
|
|
+ name={iconName}
|
|
|
|
+ userId="base"
|
|
|
|
+ theme={themeType}
|
|
|
|
+ size="2x"
|
|
|
|
+ state="normal"
|
|
|
|
+ />
|
|
|
|
+ ) : undefined
|
|
|
|
+ }
|
|
|
|
+ style={{
|
|
|
|
+ width: '1.5rem',
|
|
|
|
+ height: '1.5rem',
|
|
|
|
+ padding: 0,
|
|
|
|
+ }}
|
|
|
|
+ title={title}
|
|
|
|
+ className="truncate-text"
|
|
|
|
+ >
|
|
|
|
+ {/* 可能不需要显示文字 */}
|
|
|
|
+ </Button>
|
|
|
|
+ );
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 时间戳按钮
|
|
|
|
+const TimestampButton = ({ onClick }: { onClick: () => void }) => {
|
|
|
|
+ const themeType = useAppSelector((state) => state.theme.themeType);
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <Button
|
|
|
|
+ onClick={onClick}
|
|
|
|
+ icon={
|
|
|
|
+ <Icon
|
|
|
|
+ module="module-process"
|
|
|
|
+ name="btn_Timestamp"
|
|
|
|
+ userId="base"
|
|
|
|
+ theme={themeType}
|
|
|
|
+ size="2x"
|
|
|
|
+ state="normal"
|
|
|
|
+ />
|
|
|
|
+ }
|
|
|
|
+ style={{
|
|
|
|
+ width: '1.5rem',
|
|
|
|
+ height: '1.5rem',
|
|
|
|
+ padding: 0,
|
|
|
|
+ }}
|
|
|
|
+ title="时间戳"
|
|
|
|
+ className="truncate-text"
|
|
|
|
+ >
|
|
|
|
+ {/* 时间戳 */}
|
|
|
|
+ </Button>
|
|
|
|
+ );
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const MarkPanel = () => {
|
|
|
|
+ const dispatch = useDispatch();
|
|
|
|
+ const customMarks = useAppSelector(selectCustomMarks);
|
|
|
|
+ const selectedMark = useAppSelector(selectSelectedMark);
|
|
|
|
+ const inputText = useAppSelector(selectInputText);
|
|
|
|
+
|
|
|
|
+ const handleReturn = () => {
|
|
|
|
+ dispatch(switchToOperationPanel());
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleAddCustomMark = () => {
|
|
|
|
+ if (inputText.trim()) {
|
|
|
|
+ dispatch(addCustomMarkToList(inputText.trim()));
|
|
|
|
+ dispatch(clearInputText());
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleSelectMark = (value: string) => {
|
|
|
|
+ dispatch(setSelectedMark(value));
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handlePredefinedMarkClick = (markType: string) => {
|
|
|
|
+ // 直接触发添加预定义标记到图像的action,包含标记文本
|
|
|
|
+ dispatch({ type: 'functionArea/setAction', payload: `AddPredefinedMark:${markType}` });
|
|
|
|
+ console.log(`添加预定义标记: ${markType}`);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleTimestampClick = () => {
|
|
|
|
+ const now = new Date();
|
|
|
|
+ const timestamp = now.getFullYear() + '-' +
|
|
|
|
+ String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
|
|
|
+ String(now.getDate()).padStart(2, '0') + ' ' +
|
|
|
|
+ String(now.getHours()).padStart(2, '0') + ':' +
|
|
|
|
+ String(now.getMinutes()).padStart(2, '0') + ':' +
|
|
|
|
+ String(now.getSeconds()).padStart(2, '0');
|
|
|
|
+
|
|
|
|
+ // 触发添加时间戳到图像的action
|
|
|
|
+ dispatch({ type: 'functionArea/setAction', payload: 'AddTimestamp' });
|
|
|
|
+ console.log(`添加时间戳: ${timestamp}`);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleDeleteMark = () => {
|
|
|
|
+ // TODO: 触发删除标记的action
|
|
|
|
+ console.log('删除选中的标记');
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <Layout className="h-full">
|
|
|
|
+ {/* 顶部导航栏 */}
|
|
|
|
+ <Header
|
|
|
|
+ style={{
|
|
|
|
+ display: 'flex',
|
|
|
|
+ alignItems: 'center',
|
|
|
|
+ padding: '0 16px',
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ <Button
|
|
|
|
+ type="text"
|
|
|
|
+ icon={<ArrowLeftOutlined />}
|
|
|
|
+ onClick={handleReturn}
|
|
|
|
+ />
|
|
|
|
+ <Title level={5} style={{ margin: 0, lineHeight: '48px' }}>
|
|
|
|
+ 标记管理
|
|
|
|
+ </Title>
|
|
|
|
+ </Header>
|
|
|
|
+
|
|
|
|
+ {/* 主体内容 */}
|
|
|
|
+ <Content
|
|
|
|
+ style={{ padding: '16px', maxHeight: '100%', overflowY: 'auto' }}
|
|
|
|
+ >
|
|
|
|
+ {/* 自定义标记输入区域 */}
|
|
|
|
+ <div style={{ marginBottom: '24px' }}>
|
|
|
|
+ <Title level={5} style={{ marginBottom: '12px' }}>
|
|
|
|
+ 自定义标记
|
|
|
|
+ </Title>
|
|
|
|
+ <Flex gap="small" align="center" style={{ marginBottom: '12px' }}>
|
|
|
|
+ <Input
|
|
|
|
+ value={inputText}
|
|
|
|
+ onChange={(e) => dispatch(setInputText(e.target.value))}
|
|
|
|
+ placeholder="输入标记文本"
|
|
|
|
+ style={{ flex: 1 }}
|
|
|
|
+ onPressEnter={handleAddCustomMark}
|
|
|
|
+ />
|
|
|
|
+ <Button
|
|
|
|
+ type="primary"
|
|
|
|
+ onClick={handleAddCustomMark}
|
|
|
|
+ disabled={!inputText.trim()}
|
|
|
|
+ >
|
|
|
|
+ 添加
|
|
|
|
+ </Button>
|
|
|
|
+ </Flex>
|
|
|
|
+
|
|
|
|
+ {/* 标记选择下拉框和添加按钮 */}
|
|
|
|
+ <Flex gap="small" align="center">
|
|
|
|
+ <Select
|
|
|
|
+ value={selectedMark}
|
|
|
|
+ onChange={handleSelectMark}
|
|
|
|
+ placeholder="选择要添加的标记"
|
|
|
|
+ style={{ flex: 1 }}
|
|
|
|
+ disabled={customMarks.length === 0}
|
|
|
|
+ >
|
|
|
|
+ {customMarks.map((mark, index) => (
|
|
|
|
+ <Option key={`${mark}-${index}`} value={mark}>
|
|
|
|
+ {mark}
|
|
|
|
+ </Option>
|
|
|
|
+ ))}
|
|
|
|
+ </Select>
|
|
|
|
+ <Button
|
|
|
|
+ type="primary"
|
|
|
|
+ onClick={() => {
|
|
|
|
+ if (selectedMark) {
|
|
|
|
+ dispatch({ type: 'functionArea/setAction', payload: 'AddCustomMark' });
|
|
|
|
+ console.log(`添加自定义标记: ${selectedMark}`);
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ disabled={!selectedMark}
|
|
|
|
+ >
|
|
|
|
+ 添加
|
|
|
|
+ </Button>
|
|
|
|
+ </Flex>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <Divider />
|
|
|
|
+
|
|
|
|
+ {/* 预定义标记按钮区域 */}
|
|
|
|
+ <div style={{ marginBottom: '24px' }}>
|
|
|
|
+ <Title level={5} style={{ marginBottom: '12px' }}>
|
|
|
|
+ 预定义标记
|
|
|
|
+ </Title>
|
|
|
|
+ <Flex wrap gap="small" align="center" justify="start" className="p-1">
|
|
|
|
+ {PREDEFINED_MARKS.map((mark) => (
|
|
|
|
+ <MarkButton
|
|
|
|
+ key={mark}
|
|
|
|
+ title={mark}
|
|
|
|
+ action={mark}
|
|
|
|
+ onClick={() => handlePredefinedMarkClick(mark)}
|
|
|
|
+ />
|
|
|
|
+ ))}
|
|
|
|
+ <TimestampButton onClick={handleTimestampClick} />
|
|
|
|
+ </Flex>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <Divider />
|
|
|
|
+
|
|
|
|
+ {/* 删除标记区域 */}
|
|
|
|
+ <div style={{ marginBottom: '24px' }}>
|
|
|
|
+ <Title level={5} style={{ marginBottom: '12px' }}>
|
|
|
|
+ 标记操作
|
|
|
|
+ </Title>
|
|
|
|
+ <Flex justify="center">
|
|
|
|
+ <Button
|
|
|
|
+ danger
|
|
|
|
+ onClick={handleDeleteMark}
|
|
|
|
+ style={{ minWidth: '120px' }}
|
|
|
|
+ >
|
|
|
|
+ 删除标记
|
|
|
|
+ </Button>
|
|
|
|
+ </Flex>
|
|
|
|
+ </div>
|
|
|
|
+ </Content>
|
|
|
|
+ </Layout>
|
|
|
|
+ );
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export default MarkPanel;
|