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:
2025-11-15 15:59:08 +08:00
parent 4ba37435da
commit 25c26b2b2e
11 changed files with 1989 additions and 8 deletions

209
verify_sort.py Normal file
View File

@@ -0,0 +1,209 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
歌曲排序功能验证脚本
运行方式: python verify_sort.py
"""
import locale
from functools import cmp_to_key
# 设置本地化以支持多语言排序
locale.setlocale(locale.LC_ALL, '')
def smart_sort(title_a, title_b):
"""
智能排序函数(与 JavaScript 版本逻辑相同)
"""
def get_char_type(char):
"""判断字符类型"""
if not char:
return 3 # 空字符串排在最后
code = ord(char)
# 数字 (0-9)
if 48 <= code <= 57:
return 0
# 英文字母 (A-Z, a-z)
if (65 <= code <= 90) or (97 <= code <= 122):
return 1
# 其他所有字符(符号、中文、日文等)
return 2
# 获取首字符类型
type_a = get_char_type(title_a[0] if title_a else None)
type_b = get_char_type(title_b[0] if title_b else None)
# 首先按字符类型排序:数字 < 字母 < 其他符号
if type_a != type_b:
return type_a - type_b
# 同类型字符,使用自然排序
# Python 的 locale.strcoll 提供类似 localeCompare 的功能
try:
# 尝试数值比较(对于纯数字开头的字符串)
if type_a == 0: # 数字类型
# 提取开头的数字部分进行比较
num_a = ''
num_b = ''
for c in title_a:
if c.isdigit():
num_a += c
else:
break
for c in title_b:
if c.isdigit():
num_b += c
else:
break
if num_a and num_b:
num_cmp = int(num_a) - int(num_b)
if num_cmp != 0:
return num_cmp
# 字符串比较(忽略大小写)
return locale.strcoll(title_a.lower(), title_b.lower())
except:
# 如果 locale 比较失败,使用简单的字符串比较
if title_a.lower() < title_b.lower():
return -1
elif title_a.lower() > title_b.lower():
return 1
return 0
def get_type_label(title):
"""获取字符类型标签"""
if not title:
return '[空]'
code = ord(title[0])
if 48 <= code <= 57:
return '[数字]'
if (65 <= code <= 90) or (97 <= code <= 122):
return '[字母]'
return '[其他]'
def main():
# 测试数据
test_songs = [
"太鼓の達人",
"Zyxwv Test",
"123 Song",
"abc melody",
"456 rhythm",
"*Special*",
"10 drums",
"あいうえお",
"2 beats",
"ZZZ Final",
"1st Place",
"100 percent",
"ドンだー!",
"Battle No.1",
"~奇跡~",
"777",
"Angel Beats",
"カノン",
"Don't Stop",
"零 -ZERO-",
"3pieces",
"Apple",
"燎原ノ舞",
"99 Balloons",
"Brave Heart",
"夏祭り",
"5 Elements",
"50音",
"Zephyr",
"α wave"
]
print('\n' + '=' * 60)
print('🥁 Taiko Web - 歌曲智能排序功能测试')
print('=' * 60 + '\n')
print(f'📋 原始歌曲列表 (共 {len(test_songs)} 首):')
print('-' * 60)
for i, song in enumerate(test_songs, 1):
print(f'{i:2d}. {song}')
print('\n' + '=' * 60)
print('⚙️ 执行智能排序...\n')
# 排序
sorted_songs = sorted(test_songs, key=cmp_to_key(smart_sort))
print('✅ 排序后的歌曲列表:')
print('-' * 60)
current_type = None
for i, song in enumerate(sorted_songs, 1):
type_label = get_type_label(song)
# 检测类型变化,添加分隔符
if current_type != type_label:
if current_type is not None:
print() # 空行分隔不同类型
current_type = type_label
print(f'{i:2d}. {song:25s} {type_label}')
print('\n' + '=' * 60)
print('📊 统计信息:')
print('-' * 60)
# 统计各类型数量
number_count = sum(1 for song in sorted_songs if get_type_label(song) == '[数字]')
letter_count = sum(1 for song in sorted_songs if get_type_label(song) == '[字母]')
other_count = sum(1 for song in sorted_songs if get_type_label(song) == '[其他]')
print(f'数字开头: {number_count}')
print(f'字母开头: {letter_count}')
print(f'其他开头: {other_count}')
print(f'总计: {len(sorted_songs)}')
print('\n' + '=' * 60)
print('✨ 排序规则验证:')
print('-' * 60)
# 验证排序是否正确
is_valid = True
prev_type = -1
for song in sorted_songs:
code = ord(song[0])
if 48 <= code <= 57:
current_type = 0
elif (65 <= code <= 90) or (97 <= code <= 122):
current_type = 1
else:
current_type = 2
if current_type < prev_type:
is_valid = False
print(f'❌ 错误: "{song}" (类型 {current_type}) 出现在类型 {prev_type} 之后')
prev_type = current_type
if is_valid:
print('✅ 排序规则验证通过!')
print(' - 数字优先')
print(' - 字母次之')
print(' - 其他符号最后')
else:
print('❌ 排序规则验证失败!')
print('\n' + '=' * 60)
print('🎵 测试完成!')
print('=' * 60 + '\n')
if __name__ == '__main__':
main()