feat: 添加歌曲智能排序功能(默认启用)
- 实现智能排序:数字 -> 字母 -> 其他符号 - 添加 smartSort() 方法支持自然数值排序 - 默认启用排序功能,用户无需设置 - 支持多语言字符(中文、日文、英文等) - 添加完整的测试工具和文档 新增文件: - test_sort.html (可视化测试页面) - verify_sort.py (Python验证脚本) - verify_sort.js (Node.js验证脚本) - SORT_FEATURE.md (功能说明) - SORT_USAGE.md (使用指南) - QUICKSTART_SORT.md (快速开始) - IMPLEMENTATION_SUMMARY.md (实现总结) - CHANGELOG_SORT.md (更新日志) - UPDATE_SUMMARY.md (更新说明) 修改文件: - public/src/js/songselect.js (添加智能排序逻辑) - README.md (更新功能介绍)
This commit is contained in:
@@ -141,10 +141,11 @@ class SongSelect{
|
||||
return a.id > b.id ? 1 : -1
|
||||
}
|
||||
})
|
||||
const titlesort = localStorage.getItem("titlesort") ?? "false";
|
||||
if (titlesort === "true") {
|
||||
this.songs.sort((a, b) => a.title.localeCompare(b.title));
|
||||
}
|
||||
// 默认启用智能排序,除非用户明确禁用
|
||||
const titlesort = localStorage.getItem("titlesort") ?? "true";
|
||||
if (titlesort === "true") {
|
||||
this.songs.sort((a, b) => this.smartSort(a.title, b.title));
|
||||
}
|
||||
if(assets.songs.length){
|
||||
this.songs.push({
|
||||
title: strings.back,
|
||||
@@ -598,6 +599,50 @@ class SongSelect{
|
||||
}
|
||||
}
|
||||
|
||||
smartSort(titleA, titleB){
|
||||
// 智能排序函数:按数字、字母、其他符号的顺序排序
|
||||
// 返回值:-1 表示 titleA 在前,1 表示 titleB 在前,0 表示相等
|
||||
|
||||
// 辅助函数:判断字符类型
|
||||
const getCharType = (char) => {
|
||||
if (!char) return 3; // 空字符串排在最后
|
||||
const code = char.charCodeAt(0);
|
||||
|
||||
// 数字 (0-9)
|
||||
if (code >= 48 && code <= 57) return 0;
|
||||
|
||||
// 英文字母 (A-Z, a-z)
|
||||
if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122)) return 1;
|
||||
|
||||
// 其他所有字符(符号、中文、日文等)
|
||||
return 2;
|
||||
};
|
||||
|
||||
// 辅助函数:提取首字符并确定类型
|
||||
const getFirstCharInfo = (title) => {
|
||||
if (!title || title.length === 0) return { type: 3, char: '', title: '' };
|
||||
const firstChar = title.charAt(0);
|
||||
const type = getCharType(firstChar);
|
||||
return { type, char: firstChar, title };
|
||||
};
|
||||
|
||||
const infoA = getFirstCharInfo(titleA);
|
||||
const infoB = getFirstCharInfo(titleB);
|
||||
|
||||
// 首先按字符类型排序:数字 < 字母 < 其他符号
|
||||
if (infoA.type !== infoB.type) {
|
||||
return infoA.type - infoB.type;
|
||||
}
|
||||
|
||||
// 同类型字符,使用 localeCompare 进行自然排序
|
||||
// 这样可以正确处理数字序列(如 1, 2, 10 而不是 1, 10, 2)
|
||||
// 也能处理各种语言的字符
|
||||
return titleA.localeCompare(titleB, undefined, {
|
||||
numeric: true, // 数字按数值排序
|
||||
sensitivity: 'base' // 忽略大小写和重音符号
|
||||
});
|
||||
}
|
||||
|
||||
mouseDown(event){
|
||||
if(event.target === this.selectable || event.target.parentNode === this.selectable){
|
||||
this.selectable.focus()
|
||||
@@ -2383,7 +2428,8 @@ class SongSelect{
|
||||
y: frameTop + 640,
|
||||
w: 273,
|
||||
h: 66,
|
||||
id: "1p" + name + "\n" + rank,
|
||||
id: "1p" + name + "
|
||||
" + rank,
|
||||
}, ctx => {
|
||||
this.draw.nameplate({
|
||||
ctx: ctx,
|
||||
@@ -3166,7 +3212,8 @@ class SongSelect{
|
||||
chartParsed = true
|
||||
if(song.type === "tja"){
|
||||
promise = readFile(blob, false, "utf-8").then(dataRaw => {
|
||||
var data = dataRaw ? dataRaw.replace(/\0/g, "").split("\n") : []
|
||||
var data = dataRaw ? dataRaw.replace(/\0/g, "").split("
|
||||
") : []
|
||||
var tja = new ParseTja(data, "oni", 0, 0, true)
|
||||
for(var diff in tja.metadata){
|
||||
var meta = tja.metadata[diff]
|
||||
@@ -3177,7 +3224,8 @@ class SongSelect{
|
||||
})
|
||||
}else if(song.type === "osu"){
|
||||
promise = readFile(blob).then(dataRaw => {
|
||||
var data = dataRaw ? dataRaw.replace(/\0/g, "").split("\n") : []
|
||||
var data = dataRaw ? dataRaw.replace(/\0/g, "").split("
|
||||
") : []
|
||||
var osu = new ParseOsu(data, "oni", 0, 0, true)
|
||||
if(osu.generalInfo.AudioFilename){
|
||||
musicFilename = osu.generalInfo.AudioFilename
|
||||
@@ -3257,7 +3305,11 @@ class SongSelect{
|
||||
|
||||
toDelete() {
|
||||
// ここに削除処理を書く
|
||||
if (!confirm("本当に削除しますか?\nこの曲に問題がある場合や\n公序良俗に反する場合にのみ実行したほうがいいと思います\n本当に曲が削除されます\n成功しても反映まで1分ほどかかる場合があります")) {
|
||||
if (!confirm("本当に削除しますか?
|
||||
この曲に問題がある場合や
|
||||
公序良俗に反する場合にのみ実行したほうがいいと思います
|
||||
本当に曲が削除されます
|
||||
成功しても反映まで1分ほどかかる場合があります")) {
|
||||
return;
|
||||
}
|
||||
fetch("/api/delete", {
|
||||
|
||||
Reference in New Issue
Block a user