import React, { useState, useEffect, useMemo, useCallback, memo } from 'react'; import { bitable, CurrencyCode, FieldType, ICurrencyField, ICurrencyFieldMeta } from '@lark-base-open/js-sdk'; import { Card, Modal, Checkbox, message } from 'antd'; import './App.css'; // 选项类型定义 interface OptionGroupDef { title: string; options: string[]; required: boolean; level?: number; parentOption?: string; condition?: (checkedList: string[]) => boolean; resetOn?: string[]; multiple?: boolean; // 新增:是否支持多选,默认为 false(单选) } // 选项分组配置 const OPTION_GROUPS: OptionGroupDef[] = [ // 第一层:品牌选择 { title: '品牌', options: ['PDS客户组', 'LWH客户组', 'PLT客户组', 'RBE客户组'], required: true, level: 1 }, // PDS和LWH共用的标签树 { title: '单据类型', options: ['首单', '翻单'], required: true, level: 2, condition: (checkedList) => checkedList.includes('PDS') || checkedList.includes('LWH'), resetOn: ['PLT', 'RBE'] }, { title: '是否要打板', options: ['需要打板', '不需要打板'], required: false, level: 3, parentOption: '首单', condition: (checkedList) => (checkedList.includes('PDS')) && checkedList.includes('首单'), resetOn: ['翻单', 'PLT', 'RBE'] }, { title: '打板类型(多选)', options: ['复版', 'PP版','不用打版'], required: false, level: 3, parentOption: '首单', multiple: true, condition: (checkedList) => (checkedList.includes('LWH')) && checkedList.includes('首单'), resetOn: ['翻单', 'PLT', 'RBE'] }, { title: '翻单变动', options: ['无变动不需要修改', '有变动需要修改'], required: false, level: 3, parentOption: '翻单', condition: (checkedList) => (checkedList.includes('PDS') || checkedList.includes('LWH')) && checkedList.includes('翻单'), resetOn: ['首单', 'PLT', 'RBE'] }, { title: '特殊订单', options: ['换料寄面料样', '换料重新打板', '加色', '改尺寸重新打板'], required: false, level: 4, parentOption: '有变动需要修改', condition: (checkedList) => (checkedList.includes('PDS') || checkedList.includes('LWH')) && checkedList.includes('有变动需要修改'), resetOn: ['首单', '无变动不需要修改', 'PLT', 'RBE'] }, { title: '特殊订单', options: ['批大货布'], required: false, level: 4, parentOption: '无变动不需要修改', condition: (checkedList) => (checkedList.includes('LWH')) && checkedList.includes('无变动不需要修改'), resetOn: ['首单', '有变动需要修改', 'PLT', 'RBE'] }, { title: '批色样', options: ['寄成衣','寄色样'], required: true, level: 5, parentOption: '加色', condition: (checkedList) => (checkedList.includes('PDS') || checkedList.includes('LWH')) && checkedList.includes('加色'), resetOn: ['首单', '无变动不需要修改', '需要打板', '不需要打板', 'PLT', 'RBE'] }, { title: '批色样', options: ['要批色样', '不要批色样'], required: true, level: 4, parentOption: '不需要打板', condition: (checkedList) => (checkedList.includes('PDS') || checkedList.includes('LWH')) && checkedList.includes('首单') && checkedList.includes('不需要打板'), resetOn: ['翻单', '有变动需要修改', '无变动不需要修改', 'PLT', 'RBE'] }, { title: '品类', options: ['牛仔', '时装'], required: true, condition: (checkedList) => checkedList.includes('PDS') || checkedList.includes('LWH') }, { title: '复杂度', options: ['简单款', '基础款', '复杂款'], required: true, condition: (checkedList) => checkedList.includes('PDS') || checkedList.includes('LWH') }, { title: '二次工艺', options: ['绣花', '印花'], required: false, condition: (checkedList) => checkedList.includes('PDS') || checkedList.includes('LWH') }, { title: '是否需要批船样', options: ['不需要批船样', '需要批船样'], required: true, condition: (checkedList) => checkedList.includes('PDS') || checkedList.includes('LWH') }, { title: '运输方式', options: ['美国', '澳大利亚', '英国'], required: false, condition: (checkedList) => checkedList.includes('PDS') || checkedList.includes('LWH'), resetOn: ['PLT', 'RBE'] }, { title: '英国运输方式', options: [ '英国-海运', '英国-空运 (直飞)', '英国-空运 (转机)', '英国-铁路(中欧班列)', '英国-卡航', '英国-卡空', '英国-卡车联运', '英国-海空联运' ], required: false, level: 2, parentOption: '英国', condition: (checkedList) => (checkedList.includes('PDS') || checkedList.includes('LWH')) && checkedList.includes('英国'), resetOn: ['美国', '澳大利亚', 'PLT', 'RBE'] }, { title: '美国运输方式', options: [ '美国-海运慢船', '美国-海运快船', '美国-空运(直飞)' ], required: true, level: 2, parentOption: '美国', condition: (checkedList) => (checkedList.includes('PDS') || checkedList.includes('LWH')) && checkedList.includes('美国'), resetOn: ['英国', '澳大利亚', 'PLT', 'RBE'] }, { title: '澳大利亚运输方式', options: [ '澳大利亚-海运', '澳大利亚-空运(直飞)' ], required: true, level: 2, parentOption: '澳大利亚', condition: (checkedList) => (checkedList.includes('PDS') || checkedList.includes('LWH')) && checkedList.includes('澳大利亚'), resetOn: ['美国', '英国', 'PLT', 'RBE'] }, { title: '面料特性', options: [ '普通面料(纯棉、常规化纤)', '特殊面料(真丝、皮革、功能性面料)', '易损面料(薄纱、蕾丝)' ], required: false, condition: (checkedList) => checkedList.includes('PDS') || checkedList.includes('LWH') } // HELLOMOLLY和RBE品牌的标签树仍然为空 // 后续可根据需要添加专用标签 ]; const FABRIC_TEST_OPTIONS = ['需要面料测试', '不需要面料测试']; // 优化:预计算选项集合,避免重复includes操作 const createOptionSets = (checkedList: string[]) => { const checkedSet = new Set(checkedList); return { checkedSet, hasFirstOrder: checkedSet.has('首单'), hasReorder: checkedSet.has('翻单'), hasNoChange: checkedSet.has('无变动不需要修改'), hasChange: checkedSet.has('有变动需要修改'), hasAddColor: checkedSet.has('加色'), hasNoPlate: checkedSet.has('不需要打板') }; }; // 优化:使用memo包装OptionGroup组件 interface OptionGroupProps { group: OptionGroupDef; checkedList: string[]; onChange: (newList: string[]) => void; lockedOptions?: string[]; level?: number; } const OptionGroup = memo(({ group, checkedList, onChange, lockedOptions = [], level = 1 }) => { // 使用配置中的 multiple 属性,默认为 false(单选) const isMulti = group.multiple || false; // 优化:使用useMemo缓存计算结果 const groupChecked = useMemo(() => checkedList.filter(v => group.options.includes(v)), [checkedList, group.options] ); const indentStyle = useMemo(() => ({ marginLeft: level > 1 ? (level - 1) * 24 : 0, marginTop: level > 1 ? 8 : 0, borderLeft: level > 1 ? `${level > 2 ? 'dashed' : 'solid'} 2px #eee` : 'none', paddingLeft: level > 1 ? 12 : 0 }), [level]); const titleColor = useMemo(() => { switch(level) { case 1: return '#000'; case 2: return '#888'; case 3: return '#b36d00'; default: return '#888'; } }, [level]); const options = useMemo(() => group.options.map(opt => ({ label: opt, value: opt, disabled: lockedOptions.includes(opt) })), [group.options, lockedOptions] ); const handleChange = useCallback((list: string[]) => { const others = checkedList.filter(v => !group.options.includes(v)); let newList; if (isMulti) { // 多选模式:保留所有选中项 newList = [...others, ...list]; } else { // 单选模式:只保留最后选中的一项 newList = [...others, list.slice(-1)[0]].filter(Boolean); } onChange(newList); }, [checkedList, group.options, isMulti, onChange]); return (
{group.title} {group.required && *}
); }); // 优化:使用memo包装OrderConfigSelector组件 interface OrderConfigSelectorProps { selectedRecordId: string | null; checkedList: string[]; setCheckedList: (list: string[]) => void; onSubmit: (options: string[]) => void; onCancel: () => void; loading: boolean; } const OrderConfigSelector = memo(({ selectedRecordId, checkedList, setCheckedList, onSubmit, onCancel, loading }) => { // 优化:使用useMemo缓存可见的选项组 const visibleGroups = useMemo(() => { return OPTION_GROUPS.filter(group => { if (!group.condition) return true; return group.condition(checkedList); }); }, [checkedList]); // 优化:使用useCallback缓存事件处理函数 const handleCheckedListChange = useCallback((newList: string[]) => { const addedOptions = newList.filter(opt => !checkedList.includes(opt)); if (addedOptions.length > 0) { const resetGroups = OPTION_GROUPS.filter(group => group.resetOn?.some(resetOpt => addedOptions.includes(resetOpt)) ); if (resetGroups.length > 0) { const resetGroupsSet = new Set(resetGroups); const filteredOptions = newList.filter(opt => { const group = OPTION_GROUPS.find(g => g.options.includes(opt)); return !resetGroupsSet.has(group as OptionGroupDef); }); setCheckedList(filteredOptions); return; } } setCheckedList(newList); }, [checkedList, setCheckedList]); // 优化:使用useCallback缓存验证函数 const validateRequired = useCallback(() => { for (const group of OPTION_GROUPS) { if (group.required && group.condition?.(checkedList) !== false) { const has = checkedList.some(v => group.options.includes(v)); if (!has) { message.error(`请至少选择一项【${group.title}】`); return false; } } } // 特殊验证:如果选择了"不需要打板",必须选择是否批色样 if (checkedList.includes('首单') && checkedList.includes('不需要打板')) { const hasColorSample = checkedList.some(v => v === '要批色样' || v === '不要批色样'); if (!hasColorSample) { message.error('请选择是否要批色样'); return false; } } return true; }, [checkedList]); const needFabricTestDialog = useCallback(() => { return !(checkedList.includes('翻单') && checkedList.includes('无变动不需要修改')); }, [checkedList]); const [showFabricTestModal, setShowFabricTestModal] = useState(false); const [fabricTestSelection, setFabricTestSelection] = useState([]); const [isSubmitting, setIsSubmitting] = useState(false); const [finalOptions, setFinalOptions] = useState([]); const handleSubmit = useCallback(() => { if (!validateRequired()) return; setFinalOptions([...checkedList]); if (needFabricTestDialog()) { setFabricTestSelection([]); setShowFabricTestModal(true); } else { onSubmit([...checkedList]); } }, [validateRequired, checkedList, needFabricTestDialog, onSubmit]); const handleFabricTestSubmit = useCallback(() => { if (fabricTestSelection.length === 0) { message.error('请选择是否需要面料测试'); return; } setIsSubmitting(true); onSubmit([...finalOptions, ...fabricTestSelection]); setShowFabricTestModal(false); }, [fabricTestSelection, finalOptions, onSubmit]); const handleCancel = useCallback(() => { onCancel(); setCheckedList([]); }, [onCancel, setCheckedList]); const handleFabricTestChange = useCallback((values: string[]) => { if (values.length > 0) { setFabricTestSelection([values[values.length - 1]]); } else { setFabricTestSelection([]); } }, []); useEffect(() => { if (!showFabricTestModal && isSubmitting) { setIsSubmitting(false); } }, [showFabricTestModal, isSubmitting]); return ( <> {visibleGroups.map(group => ( ))} setShowFabricTestModal(false)} okText="确定" cancelText="取消" confirmLoading={isSubmitting} >

请选择是否需要面料测试:

); }); // 优化:主应用组件 const App: React.FC = () => { const [selectedRecordId, setSelectedRecordId] = useState(null); const [checkedList, setCheckedList] = useState([]); const [loading, setLoading] = useState(false); const [currentSelection, setCurrentSelection] = useState<{ tableId: string | null; recordId: string | null; }>({ tableId: null, recordId: null }); // 优化:缓存字段选项映射,避免重复查找 const [fieldOptionsMap, setFieldOptionsMap] = useState>(new Map()); // 优化:使用useCallback缓存函数 const resetOptions = useCallback(() => { setSelectedRecordId(null); setCheckedList([]); }, []); const saveOptions = useCallback(async () => { if (!selectedRecordId || !currentSelection.tableId) { message.warning('没有有效的选中记录或表格信息'); return; } setLoading(true); const FIELD_ID_TO_SAVE = 'fldTtRHwlo'; try { const table = await bitable.base.getTableById(currentSelection.tableId); if (!table) { message.error('无法获取表格对象'); return; } const field = await table.getFieldById(FIELD_ID_TO_SAVE); if (!field) { message.error('无法获取字段对象'); return; } // 优化:只在字段选项映射为空时才重新获取 let optionsMap = fieldOptionsMap; if (optionsMap.size === 0) { const fieldMeta = await field.getMeta(); console.log('字段元数据:', fieldMeta); // 检查字段类型是否为多选或单选字段 let fieldOptions = []; if (fieldMeta.type === FieldType.MultiSelect || fieldMeta.type === FieldType.SingleSelect) { fieldOptions = (fieldMeta.property as any).options || []; } optionsMap = new Map(fieldOptions.map(opt => [opt.name, opt])); setFieldOptionsMap(optionsMap); } let dataToSave = null; if (checkedList.length > 0) { console.log('字段中的所有选项:', Array.from(optionsMap.keys())); console.log('要保存的选项:', checkedList); // 优化:使用Map查找,O(1)时间复杂度 dataToSave = checkedList.map(optionText => { const matchedOption = optionsMap.get(optionText); if (!matchedOption) { console.warn(`未找到选项 "${optionText}" 对应的ID`); console.log('可能的匹配选项:', Array.from(optionsMap.keys()).filter(key => key.includes(optionText) || optionText.includes(key) )); return null; } console.log(`找到匹配: "${optionText}" -> ID: ${matchedOption.id}`); return { id: matchedOption.id, text: matchedOption.name }; }).filter(Boolean); } console.log('准备保存的数据:', dataToSave); await field.setValue(selectedRecordId, dataToSave); const savedValue = await field.getValue(selectedRecordId); console.log('保存后的字段值:', savedValue); if (savedValue) { console.log('数据成功写入单元格'); message.success('已保存选项'); } else { throw new Error('数据写入后未能读取到值'); } } catch (e: any) { console.error('保存失败:', e); message.error(`保存失败: ${e.message || '未知错误'}`); } finally { setLoading(false); resetOptions(); setCurrentSelection({ tableId: null, recordId: null }); } }, [selectedRecordId, currentSelection.tableId, checkedList, fieldOptionsMap, resetOptions]); useEffect(() => { const unsubscribe = bitable.base.onSelectionChange(async (event: any) => { try { const { data } = event; if (data && data.tableId && data.recordId) { const isNewCell = ( data.tableId !== currentSelection.tableId || data.recordId !== currentSelection.recordId ); if (isNewCell) { resetOptions(); setCurrentSelection({ tableId: data.tableId, recordId: data.recordId }); // 优化:切换单元格时清空字段选项缓存 setFieldOptionsMap(new Map()); } const table = await bitable.base.getTableById(data.tableId); const cellValue = await table.getCellValue('fldTtRHwlo', data.recordId); setSelectedRecordId(data.recordId); if (typeof cellValue === 'string' && cellValue.trim() !== '') { setCheckedList(cellValue.split(',').map(item => item.trim())); } else if (Array.isArray(cellValue)) { setCheckedList(cellValue.map(item => String(item))); } else { setCheckedList([]); } } } catch (e: any) { message.error('获取选中记录失败: ' + (e.message || e)); } }); return unsubscribe; }, [currentSelection, resetOptions]); return (
请先在多维表格中点击一个单元格以加载配置。
); }; export default App;