废话:
最近在对 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 18:36:37 +08:00 via Android
只要取到目标前后对象的 sort 值,然后将这这两个值和的一半写入目标就可以了
|
6
rizon OP |