1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/**
 *
 * @author chen.lin
 * @time 2026-02-02
 * 库区排序编辑组件
 * 功能:显示已选中的库区,允许修改排序值
 * 数据格式:[{"id": 1, "sort": 1}, {"id": 2, "sort": 2}]
 * 
 * 使用隐藏字段存储排序信息,保存时合并到 areas 字段
 */
import React, { useState, useEffect, useRef, useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
import {
    Box,
    TextField,
    Chip,
    IconButton,
    Paper,
    Typography,
    Stack,
} from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
import request from '@/utils/request';
const AreasSortInput = ({ source }) => {
    const { setValue, watch } = useFormContext();
    const sortSource = `${source}Sort`; // 隐藏字段名:areasSort
    const [areas, setAreas] = useState([]);
    const [selectedAreas, setSelectedAreas] = useState([]);
    const [loading, setLoading] = useState(false);
    const currentValue = watch(source) || [];
    const prevSelectedAreasRef = useRef([]);
 
    // 加载所有库区选项(用于获取名称)
    useEffect(() => {
        const loadAreas = async () => {
            setLoading(true);
            try {
                const res = await request.post('/warehouseAreas/list', {});
                if (res?.data?.code === 200) {
                    setAreas(res.data.data || []);
                } else {
                    console.error('加载库区失败:', res?.data?.msg);
                }
            } catch (error) {
                console.error('加载库区失败:', error);
            } finally {
                setLoading(false);
            }
        };
        loadAreas();
    }, []);
 
    // 初始化选中项:将后端数据格式转换为前端格式
    useEffect(() => {
        // currentValue 现在应该是纯ID数组 [1, 2, 3],因为 ReferenceArrayInput 的 format 已经处理了
        if (currentValue && currentValue.length > 0) {
            // 检查是否是纯ID数组
            const isIdArray = currentValue.every(item => typeof item === 'number' || typeof item === 'string');
            
            if (isIdArray) {
                // 纯ID数组格式 [1, 2, 3],需要与现有数据合并
                const currentIds = new Set(currentValue.map(id => Number(id)));
                
                // 保留已有的排序信息(如果存在)
                const existingAreas = prevSelectedAreasRef.current.filter(item => 
                    currentIds.has(Number(item.id))
                );
                const existingIds = new Set(existingAreas.map(item => Number(item.id)));
                
                // 找出新增的ID
                const newIds = currentValue
                    .map(id => Number(id))
                    .filter(id => !existingIds.has(id));
                
                // 为新增的ID创建排序项(默认排序为已有最大排序值+1)
                const maxSort = existingAreas.length > 0 
                    ? Math.max(...existingAreas.map(item => item.sort || 1), 0)
                    : 0;
                const newItems = newIds.map((id, index) => ({
                    id: id,
                    sort: maxSort + index + 1,
                }));
                
                // 合并已有项和新项
                const converted = [...existingAreas, ...newItems];
                setSelectedAreas(converted);
                prevSelectedAreasRef.current = converted;
                // 保存排序信息到隐藏字段
                setValue(sortSource, converted, { shouldValidate: false });
            } else {
                // 如果已经是对象数组格式 [{id, sort}](从后端直接加载的情况)
                const sorted = [...currentValue].sort((a, b) => {
                    const sortA = a.sort || 0;
                    const sortB = b.sort || 0;
                    return sortA - sortB;
                });
                setSelectedAreas(sorted);
                prevSelectedAreasRef.current = sorted;
                // 保存排序信息到隐藏字段
                setValue(sortSource, sorted, { shouldValidate: false });
                // 同时更新 ReferenceArrayInput 的值为纯ID数组
                const ids = sorted.map(item => item.id);
                setValue(source, ids, { shouldValidate: false });
            }
        } else {
            setSelectedAreas([]);
            prevSelectedAreasRef.current = [];
            // 清空排序信息
            setValue(sortSource, [], { shouldValidate: false });
        }
    }, [currentValue, source, setValue]);
 
    // 处理删除库区
    const handleDeleteArea = (id) => {
        const filtered = selectedAreas.filter(item => item.id !== id);
        setSelectedAreas(filtered);
        prevSelectedAreasRef.current = filtered;
        // 更新表单值为纯ID数组(ReferenceArrayInput 需要)
        const ids = filtered.map(item => item.id);
        setValue(source, ids, { shouldValidate: true, shouldDirty: true, shouldTouch: true });
        // 更新排序信息
        setValue(sortSource, filtered, { shouldValidate: false, shouldDirty: true, shouldTouch: true });
    };
 
    // 处理修改排序值
    const handleSortChange = (id, newSort) => {
        const sortValue = parseInt(newSort) || 1;
        const updated = selectedAreas.map(item => {
            if (item.id === id) {
                return { ...item, sort: sortValue };
            }
            return item;
        });
        // 按排序值排序
        const sorted = [...updated].sort((a, b) => {
            const sortA = a.sort || 0;
            const sortB = b.sort || 0;
            return sortA - sortB;
        });
        setSelectedAreas(sorted);
        prevSelectedAreasRef.current = sorted;
        // 更新排序信息到隐藏字段,设置 shouldDirty: true 以触发表单的 dirty 状态
        setValue(sortSource, sorted, { shouldValidate: false, shouldDirty: true, shouldTouch: true });
    };
 
    // 获取库区名称
    const getAreaName = (id) => {
        if (!id) return '未知';
        const area = areas.find(a => a.id === id);
        return area ? area.name : `ID: ${id}`;
    };
 
    // 确保列表始终按排序值排序
    const sortedAreas = useMemo(() => {
        if (!selectedAreas || selectedAreas.length === 0) {
            return [];
        }
        return [...selectedAreas].sort((a, b) => {
            const sortA = a.sort || 0;
            const sortB = b.sort || 0;
            return sortA - sortB;
        });
    }, [selectedAreas]);
 
    // 如果没有选中的库区,不显示
    if (!sortedAreas || sortedAreas.length === 0) {
        return null;
    }
 
    return (
        <Box sx={{ mt: 2 }}>
            <Typography 
                variant="body2" 
                sx={{ 
                    mb: 1, 
                    color: 'text.secondary',
                    width: '100%',
                }}
            >
                已选库区(可修改排序):
            </Typography>
            
            {/* 表头 */}
            <Paper
                elevation={0}
                sx={{
                    p: 1,
                    display: 'flex',
                    alignItems: 'center',
                    gap: 2,
                    width: '100%',
                    bgcolor: 'grey.100',
                    mb: 0.5,
                }}
            >
                <Box sx={{ flex: 1, minWidth: 0 }}>
                    <Typography variant="body2" sx={{ fontWeight: 'medium' }}>
                        库区
                    </Typography>
                </Box>
                <Box sx={{ width: 120, textAlign: 'center' }}>
                    <Typography variant="body2" sx={{ fontWeight: 'medium' }}>
                        排序
                    </Typography>
                </Box>
                <Box sx={{ width: 48 }}></Box> {/* 删除按钮占位 */}
            </Paper>
            
            <Stack spacing={1}>
                {sortedAreas.map((item) => {
                    // 确保 item.id 存在且有效
                    if (!item || item.id === undefined || item.id === null) {
                        return null;
                    }
                    return (
                        <Paper
                            key={item.id}
                            elevation={1}
                            sx={{
                                p: 1.5,
                                display: 'flex',
                                alignItems: 'center',
                                gap: 2,
                                width: '100%',
                                height: '30px',
                                minHeight: 'auto',
                                maxWidth: '100%',
                                boxSizing: 'border-box',
                            }}
                        >
                            {/* 左边:库区名称 */}
                            <Box sx={{ flex: 1, minWidth: 0 }}>
                                <Chip
                                    label={getAreaName(item.id)}
                                    size="small"
                                    sx={{ maxWidth: '100%' }}
                                />
                            </Box>
                            
                            {/* 右边:排序输入框(无 label) */}
                            <TextField
                                type="number"
                                value={item.sort || ''}
                                onChange={(e) => handleSortChange(item.id, e.target.value)}
                                size="small"
                                placeholder="排序"
                                sx={{ 
                                    width: 120,
                                    '& .MuiInputBase-root': {
                                        height: '30px',
                                    },
                                    '& .MuiInputBase-input': {
                                        height: '30px',
                                        padding: '8.5px 14px',
                                    }
                                }}
                                inputProps={{ 
                                    min: 1,
                                    step: 1
                                }}
                            />
                            
                            {/* 删除按钮 */}
                            <IconButton
                                size="small"
                                onClick={() => handleDeleteArea(item.id)}
                                color="error"
                            >
                                <DeleteIcon fontSize="small" />
                            </IconButton>
                        </Paper>
                    );
                })}
            </Stack>
        </Box>
    );
};
 
export default AreasSortInput;