当前位置: 首页 > web >正文

checkBox支持拖拉拽调动位置,改变布局和选择值的顺序

ZoneSelect.tsx组件编写

import React, { useState, useMemo } from 'react';
import { Checkbox } from 'antd';
import {DndContext,closestCenter,KeyboardSensor,PointerSensor,useSensor,useSensors,DragOverlay,
} from '@dnd-kit/core';
import {arrayMove,SortableContext,horizontalListSortingStrategy,useSortable,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';interface ZoneOption {name: string;display: string;
}interface ZoneSelectProps {value?: string;onChange?: (value: string) => void;onOrderChange?: (order: string[]) => void;initialOrder?: string[];
}export const INITIAL_ZONES: ZoneOption[] = [{ name: 'auto', display: 'Auto-Select' },{ name: 'eu', display: 'Europe' },{ name: 'hk', display: 'Hong Kong' },{ name: 'ja', display: 'Japan' },{ name: 'ko', display: 'Korea' },{ name: 'na', display: 'North America' },{ name: 'sg', display: 'Singapore' },
];const SortableItem: React.FC<{id: string;zone: ZoneOption;checked: boolean;onChange: (checked: boolean) => void;
}> = ({ id, zone, checked, onChange }) => {const {attributes,listeners,setNodeRef,transform,transition,isDragging,} = useSortable({ id });const style = {transform: CSS.Transform.toString(transform),transition,opacity: isDragging ? 0.8 : 1,zIndex: isDragging ? 1 : 0,};return (<divref={setNodeRef}style={{...style,width: '150px',marginBottom: '4px',}}{...attributes}><div {...listeners} style={{ cursor: 'grab', padding: '4px 0' }}><Checkboxchecked={checked}onChange={(e) => {e.stopPropagation();onChange(e.target.checked);}}>{zone.display}</Checkbox></div></div>);
};const ZoneSelect: React.FC<ZoneSelectProps> = ({value = '[]',onChange,initialOrder,onOrderChange}) => {// 初始化区服顺序const [zones, setZones] = useState<ZoneOption[]>(() => {// 1. 优先使用传入的initialOrderif (initialOrder && initialOrder.length > 0) {const orderedZones = initialOrder.map(name => INITIAL_ZONES.find(z => z.name === name)).filter(Boolean) as ZoneOption[];// 补全可能缺失的区服const missingZones = INITIAL_ZONES.filter(zone => !initialOrder.includes(zone.name));return [...orderedZones, ...missingZones];}return INITIAL_ZONES;});const [activeId, setActiveId] = useState<string | null>(null);// 解析选中的值const selectedZones = useMemo(() => {try {return JSON.parse(value) || [];} catch {return [];}}, [value]);const sensors = useSensors(useSensor(PointerSensor, {activationConstraint: {distance: 5, // 需要移动5px才触发拖拽},}),useSensor(KeyboardSensor));const handleDragStart = (event: any) => {setActiveId(event.active.id);};const handleDragEnd = (event: any) => {const { active, over } = event;setActiveId(null);if (over && active.id !== over.id) {const oldIndex = zones.findIndex((zone) => zone.name === active.id);const newIndex = zones.findIndex((zone) => zone.name === over.id);const newZones = arrayMove(zones, oldIndex, newIndex);setZones(newZones);// 1. 通知外部排序变化const newOrder = newZones.map(z => z.name);onOrderChange?.(newOrder);// 2. 更新选中项的顺序(按照新顺序)const orderedSelected = newZones.filter(z => selectedZones.some(sz => sz.name === z.name)).map(z => ({name: z.name,display: z.display}));onChange?.(JSON.stringify(orderedSelected));}};const handleCheckboxChange = (zoneName: string, checked: boolean) => {const zone = zones.find((z) => z.name === zoneName);if (!zone) return;const newSelected = checked? [...selectedZones, zone]: selectedZones.filter((z) => z.name !== zoneName);// 按照当前 zones 顺序返回选中项const orderedSelected = zones.filter(z => newSelected.some(sz => sz.name === z.name)).map(z => ({name: z.name,display: z.display}));onChange?.(JSON.stringify(orderedSelected));};const activeZone = activeId ? zones.find((zone) => zone.name === activeId) : null;return (<DndContextsensors={sensors}collisionDetection={closestCenter}onDragStart={handleDragStart}onDragEnd={handleDragEnd}><SortableContext items={zones} strategy={horizontalListSortingStrategy}><div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>{zones.map((zone) => (<SortableItemkey={zone.name}id={zone.name}zone={zone}checked={selectedZones.some((z) => z.name === zone.name)}onChange={(checked) => handleCheckboxChange(zone.name, checked)}/>))}</div></SortableContext><DragOverlay>{activeZone ? (<divstyle={{width: '250px',background: 'rgba(255, 255, 255, 0.9)',boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',borderRadius: '4px',padding: '4px',transform: 'scale(1.02)',}}><Checkbox checked={selectedZones.some((z) => z.name === activeZone.name)}>{activeZone.display}</Checkbox></div>) : null}</DragOverlay></DndContext>);
};export default ZoneSelect;

http://www.xdnf.cn/news/19744.html

相关文章:

  • MySQL 事务的“暗面”与“高光”:故障、调优与案例复盘
  • 网络编程 socket——TCP
  • Leetcode 3665. Twisted Mirror Path Count
  • 江协科技STM32学习笔记补充之001。为什么C语言在对STM32编程过程中的二进制要用十六进制来进行读写。而不能直接用二进制来进行读写。
  • “人工智能+”时代的端侧AI:算力下沉与实时视频的新基座
  • 36. Ansible变量+管理机密
  • leetcode-python-1796字符串中第二大的数字
  • Python OpenCV图像处理与深度学习:Python OpenCV对象检测入门-Haar级联分类器与人脸检测
  • SpringCloud框架组件梳理
  • SQL Server从入门到项目实践(超值版)读书笔记 25
  • go语言面试之Goroutine 数量控制, GC回收 和任务调度
  • JimuReport 积木报表 v2.1.3 版本发布,免费开源的可视化报表和大屏
  • 2025 金融行业证书怎么选?从能力适配到职业方向的理性梳理
  • 别让你的 AI 对话烂在聊天记录里!
  • 马健涛事件折射出中国音乐产业转型期的深层矛盾,最终解决之道在于完善我国音乐版权鉴定的技术标准
  • Linux系统之----客户端服务器设计(共享内存)
  • 一文通透!为什么 DBSCAN 能检测任意形状的簇 ?
  • 【开题答辩全过程】以 校园帮帮团跑腿系统的设计与实现为例,包含答辩的问题和答案
  • Redis持久化:RDB与AOF,五分钟快速掌握
  • React 第七十一节 Router中generatePath的使用详解及注意事项
  • 1. 从零开始搭建微服务架构1.0(登录模块)
  • 首屏优化讲解
  • springboot:数据校验
  • 【光照】Unity中的[光照模型]概念辨析
  • nginx关于root
  • AI使用指南:9月开学季,自动生成教学PPT
  • 基于 GEE 探索太湖区域 2010—2020 年增强型植被指数 EVI 时空变化
  • dify安装和配置
  • JS循环机制
  • 【ARMv7】开篇:掌握ARMv7架构Soc开发技能