
废话:
最近在对 notelive.cc 这个网站做一个新版本(不会替代这个网站,只是做一个新项目),然后遇到了目录树的排序老问题。
正题:
出于降低数据库写压力,简化初始化数据结构,以及我就是不喜欢排序要修改两个对象的个人原因等,因此做了这个排序模型。 实现效果:
有什么好的优化建议,也欢迎贴出来。
"use client"; import { useRef, useState } from "react"; interface Item { id: number; orderTime: number | undefined; pre: number | null | undefined; } let init: Item[] = [ { id: 1, orderTime: undefined, pre: undefined, }, { id: 4, orderTime: undefined, pre: undefined, }, { id: 2, orderTime: undefined, pre: 3, }, { id: 3, orderTime: undefined, pre: undefined, }, ]; export default function SortPage() { const source = useRef<HTMLInputElement>(null); const target = useRef<HTMLInputElement>(null); const [list, setList] = useState<Item[]>(init); const doSort = () => { if (!source.current || !source.current.value) { setList(reSort(list)); return; } const sourceItem = list.find( (item) => item.id == parseInt(source.current!.value) ); if (!sourceItem) { alert("not fount sourceItem"); return; } const targetItem = target.current!.value ? list.find((item) => item.id == parseInt(target.current!.value)) : null; // 只需要修改 sourceItem 的值,不用处理 targetItem if (!targetItem) { sourceItem.orderTime = Date.now(); sourceItem.pre = -1; } else { sourceItem.orderTime = Date.now(); sourceItem.pre = targetItem.id; } setList(reSort(list)); }; return ( <div> <div> <div> 移动的元素: <input className="border" ref={source} type="text" /> </div> <div> 目标元素的后面(留空首位):{" "} <input className="border" ref={target} type="text" /> </div> <div> <button className="bg-gray-400 p-2" OnClick={doSort}> 移动 </button> </div> </div> <div className="mt-5 flex items-center justify-start gap-x-2 px-3"> {list.map((item) => { return ( <div className="bg-blue-100 p-2 " key={item.id}> <p>id: {item.id}</p> <p>orderTime: {item.orderTime}</p> <p>pre: {item.pre}</p> </div> ); })} </div> </div> ); } function reSort(originList: Item[]) { const list = [...originList]; const sortById = (pre: Item, next: Item) => { // id 从小到大排序 if (pre.id > next.id) { return 1; } else if (pre.id < next.id) { return -1; } return 0; }; const sortByOrderTime = (pre: Item, next: Item) => { let preOrderTime = pre.orderTime || 0; let nextOrderTime = next.orderTime || 0; // orderTime 从小到大排序 if (preOrderTime > nextOrderTime) { return 1; } else if (preOrderTime < nextOrderTime) { return -1; } return 0; }; list.sort(sortById).sort(sortByOrderTime); const checkList = [...list]; // 按照 orderTime 从旧到新,每个元素都执行一次 pre 位置调整处理,如果两个元素的 pre 相同,orderTime 最晚的则会插入到旧数据的前面 for (let i = 0; i < checkList.length; i++) { const checkItem = checkList[i]; if (checkItem.pre) { moveElementAfter(list, checkItem.id, checkItem.pre); } } function moveElementAfter( array: Item[], sourceId: number, targetItemId: number ) { const sourceIdx = array.findIndex((e) => e.id === sourceId); if (sourceIdx === -1) { return; } const sourceItem = array[sourceIdx]; const indexToInsert = list.findIndex((e) => e.id === targetItemId); array.splice(sourceIdx, 1); if (indexToInsert > -1) { array.splice(indexToInsert + 1, 0, sourceItem); // 在目标元素后面插入 } else { array.unshift(sourceItem); // 如果找不到元素 b ,则将元素 a 放在数组首位 } } return list; } 文末: 如果大家对 notelive.cc 这个网站有什么建议,或对正在内测的新笔记产品有兴趣,或单纯想要唠唠嗑,可以加微信群哈。

1 rizon OP 擦 有问题,有些场景不对。忽略吧 |
2 renmu 2023 年 12 月 10 日 via Android 只要取到目标前后对象的 sort 值,然后将这这两个值和的一半写入目标就可以了 |
6 rizon OP |