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
import React, { useState, useEffect } from 'react';
import { useFormContext } from 'react-hook-form';
import {
    Box,
    TextField,
    Autocomplete,
    Chip,
    IconButton,
    Paper,
    Typography,
    Stack,
} from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
import request from '@/utils/request';
 
/**
 * @author chen.lin
 * @time 2026-02-02
 * 可排序的库区选择组件
 * 数据格式:[{"id": 1, "sort": 1}, {"id": 2, "sort": 2}]
 * 
 * 功能:
 * 1. 上方:多选框选择库区
 * 2. 下方:显示已选中的库区,每行显示库区名称和排序输入框
 * 3. 默认排序为1,可以修改排序值
 */
const SortableAreasInput = ({ source, label, validate, ...props }) => {
    const { setValue, watch } = useFormContext();
    const [areas, setAreas] = useState([]);
    const [selectedAreas, setSelectedAreas] = useState([]);
    const [loading, setLoading] = useState(false);
    const currentValue = watch(source) || [];
 
    // 加载所有库区选项
    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(() => {
        if (currentValue && currentValue.length > 0) {
            // 如果已经是对象数组格式 [{id, sort}]
            if (typeof currentValue[0] === 'object' && currentValue[0].id !== undefined) {
                const sorted = [...currentValue].sort((a, b) => {
                    const sortA = a.sort || 0;
                    const sortB = b.sort || 0;
                    return sortA - sortB;
                });
                setSelectedAreas(sorted);
            } else {
                // 如果是旧格式 [1, 2, 3],转换为新格式,默认排序为1
                const converted = currentValue.map((id, index) => ({
                    id: id,
                    sort: index + 1,
                }));
                setSelectedAreas(converted);
                // 同步更新表单值
                setValue(source, converted, { shouldValidate: false });
            }
        } else {
            setSelectedAreas([]);
        }
    }, [currentValue, source, setValue]);
 
    // 处理添加库区
    const handleAddArea = (event, newValue) => {
        if (newValue && !selectedAreas.find(item => item.id === newValue.id)) {
            // 默认排序为1,如果已有数据,使用最大排序值+1
            const maxSort = selectedAreas.length > 0 
                ? Math.max(...selectedAreas.map(item => item.sort || 1))
                : 0;
            const newItem = {
                id: newValue.id,
                sort: maxSort + 1,
            };
            const updated = [...selectedAreas, newItem];
            setSelectedAreas(updated);
            setValue(source, updated, { shouldValidate: true });
        }
    };
 
    // 处理删除库区
    const handleDeleteArea = (id) => {
        const filtered = selectedAreas.filter(item => item.id !== id);
        setSelectedAreas(filtered);
        setValue(source, filtered, { shouldValidate: 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;
        });
        setSelectedAreas(updated);
        setValue(source, updated, { shouldValidate: true });
    };
 
    // 获取库区名称
    const getAreaName = (id) => {
        const area = areas.find(a => a.id === id);
        return area ? area.name : `ID: ${id}`;
    };
 
    // 过滤已选中的选项
    const availableOptions = areas.filter(
        area => !selectedAreas.find(item => item.id === area.id)
    );
 
    return (
        <Box>
            {/* 多选框 */}
            <Autocomplete
                multiple
                options={availableOptions}
                getOptionLabel={(option) => option.name || ''}
                onChange={handleAddArea}
                loading={loading}
                value={[]} // 始终为空,因为已选中的在下方面板显示
                renderInput={(params) => (
                    <TextField
                        {...params}
                        label={label}
                        placeholder="选择库区..."
                        variant="outlined"
                        size="small"
                    />
                )}
                sx={{ mb: 2 }}
            />
 
            {/* 已选中的库区列表,显示键值对(库区名称:排序值) */}
            {selectedAreas.length > 0 && (
                <Box sx={{ mt: 2 }}>
                    <Typography variant="body2" sx={{ mb: 1, color: 'text.secondary' }}>
                        已选库区(可修改排序):
                    </Typography>
                    <Stack spacing={1}>
                        {selectedAreas.map((item) => (
                            <Paper
                                key={item.id}
                                elevation={1}
                                sx={{
                                    p: 1.5,
                                    display: 'flex',
                                    alignItems: 'center',
                                    gap: 2,
                                }}
                            >
                                {/* 左边:库区名称 */}
                                <Box sx={{ flex: 1, minWidth: 0 }}>
                                    <Chip
                                        label={getAreaName(item.id)}
                                        size="small"
                                        sx={{ maxWidth: '100%' }}
                                    />
                                </Box>
                                
                                {/* 右边:排序输入框 */}
                                <TextField
                                    type="number"
                                    label="排序"
                                    value={item.sort || ''}
                                    onChange={(e) => handleSortChange(item.id, e.target.value)}
                                    size="small"
                                    sx={{ width: 120 }}
                                    inputProps={{ 
                                        min: 1,
                                        step: 1
                                    }}
                                />
                                
                                {/* 删除按钮 */}
                                <IconButton
                                    size="small"
                                    onClick={() => handleDeleteArea(item.id)}
                                    color="error"
                                >
                                    <DeleteIcon fontSize="small" />
                                </IconButton>
                            </Paper>
                        ))}
                    </Stack>
                </Box>
            )}
        </Box>
    );
};
 
export default SortableAreasInput;