import React, { useEffect, useRef, useState } from 'react'; import { Button, Form, Input, Modal, Popconfirm, Select, Spin, Steps, Tag, Typography } from 'antd'; import { CloudUploadOutlined, DeleteOutlined, EditOutlined, LinkOutlined, PlusOutlined, SaveOutlined, SearchOutlined, } from '@ant-design/icons'; import api from '../api'; import { message } from '../ui/app-message'; import { usePermission } from '../hooks/usePermission'; import PermissionButton from './PermissionButton'; const { Text } = Typography; // ── Types ── interface JinshujuEntryRow { serialNumber: number; name: string; phone: string | null; suggestedStudent: { id: number; name: string; phone: string | null; studentNo: string | null; } | null; } interface StudentOption { id: number; name: string; phone: string | null; studentNo: string | null; } interface PreviewResponse { success: boolean; entries: JinshujuEntryRow[]; students: StudentOption[]; } interface MatchRule { id: number; name: string; formToken: string; mappings: Record; createdAt: string; } interface JinshujuFormField { key: string; label: string; type: string; } type MatchDecision = | { action: 'match'; matchStudentId: number } | { action: 'create'; createName: string; createPhone: string } | { action: 'skip' }; // ── Constants ── const ROW_HEIGHT = 72; const LEFT_WIDTH = 260; const GAP = 80; const STUDENT_FIELDS = [ { key: 'name', label: '姓名' }, { key: 'phone', label: '手机号' }, { key: 'idNumber', label: '身份证号' }, { key: 'gender', label: '性别' }, { key: 'ethnicity', label: '民族' }, { key: 'emergencyContact', label: '紧急联系人' }, { key: 'emergencyPhone', label: '紧急联系电话' }, { key: 'studentNo', label: '学号' }, ]; // ── MatchSelector sub-component ── interface MatchSelectorProps { entry: JinshujuEntryRow; decision: MatchDecision | undefined; studentOptions: StudentOption[]; onChange: (d: MatchDecision) => void; } const MatchSelector: React.FC = ({ entry, decision, studentOptions, onChange, }) => { const action = decision?.action ?? 'skip'; if (action === 'match') { const matchD = decision as { action: 'match'; matchStudentId: number }; const matchedStudent = studentOptions.find((s) => s.id === matchD.matchStudentId); return (
}> 已匹配 {matchedStudent?.name ?? '未知'} {matchedStudent?.studentNo && ( ({matchedStudent.studentNo}) )}
); } if (action === 'create') { const createD = decision as { action: 'create'; createName: string; createPhone: string }; return (
}> 将新建 onChange({ action: 'create', createName: e.target.value, createPhone: createD.createPhone, }) } /> onChange({ action: 'create', createName: createD.createName, createPhone: e.target.value, }) } />
); } return (
setName(e.target.value)} style={{ marginBottom: 12 }} /> 选择金数据字段映射到学生资料 {STUDENT_FIELDS.map((sf) => (
{sf.label} ); const renderRuleStep = () => (
选择匹配规则 已连接表单「{formName}」,共 {formFields.length} 个字段。选择已有规则或新建字段映射。