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;
|