feat(admin): 图片歌单改导入酷狗+自动排入按期望时段+日期滚动修复
1. 菜单重命名: 图片歌单 -> 导入酷狗 (Sidebar/Dashboard/教程 + 页面标题) 2. PlaylistCardPage(导入酷狗) 新增: - 粘贴酷狗歌单文本解析(支持序号/横杠/括号格式) - 预览区歌名与歌手名完整显示(去除truncate, 支持换行展示) - 选日期+选播出时段, 一键将解析后的歌曲(没找到则自动创建)生成为该日期的排期 - 批量保存后展示状态徽章(匹配/创建/失败) 3. PlaylistCardGenerator 画布: - 去除 substring(0,12)/substring(0,10) 截断, 改用 fitText 按 canvas 宽度逐字符换行切分 - canvas 高度根据实际行数动态计算, 最多支持 200 首 - 中文字体堆栈加入 PingFang SC/Microsoft YaHei 4. ScheduleManager.autoScheduleExpected: - 优先按歌曲自己的 preferredPlayTimeId(期望播出时段) 分组排入 - 未指定才 fallback 到顶部当前选中时段 - 分时段各自维护 sequence, 最后按时段 startTime 升序+分序 合并重排 - 顶部过滤了单时段时, 只排入匹配该时段的歌曲(含未指定fallback) 5. 日期滚动修复: - AdminDatePicker / AdminMultiDatePicker scrollToDateElement 弃用 scrollIntoView - 改用精确的 offsetLeft - (clientWidth-offsetWidth)/2 计算 scrollTo left 值, 避免受父级滚动容器/左箭头影响
This commit is contained in:
@@ -2075,17 +2075,36 @@ const autoScheduleExpected = () => {
|
||||
}
|
||||
|
||||
const baseId = Date.now()
|
||||
const filteredPtId = selectedPlayTime.value ? parseInt(selectedPlayTime.value) : null
|
||||
|
||||
const playTimeSeqCounters = new Map()
|
||||
for (const s of localScheduledSongs.value) {
|
||||
const key = String(s.playTimeId || '')
|
||||
playTimeSeqCounters.set(key, (playTimeSeqCounters.get(key) || 0) + 1)
|
||||
}
|
||||
|
||||
expectedSongs.forEach((song, idx) => {
|
||||
const existingIndex = localScheduledSongs.value.findIndex((s) => s.song?.id === song.id)
|
||||
if (existingIndex !== -1) return
|
||||
|
||||
const playTimeId = selectedPlayTime.value ? parseInt(selectedPlayTime.value) : null
|
||||
let targetPlayTimeId = null
|
||||
if (song.preferredPlayTimeId) {
|
||||
const matched = (playTimes.value || []).find(pt => pt.id === song.preferredPlayTimeId)
|
||||
if (matched) targetPlayTimeId = song.preferredPlayTimeId
|
||||
}
|
||||
if (!targetPlayTimeId && filteredPtId != null) targetPlayTimeId = filteredPtId
|
||||
|
||||
if (filteredPtId != null && targetPlayTimeId !== filteredPtId) return
|
||||
const ptKey = String(targetPlayTimeId || '')
|
||||
const nextSeq = (playTimeSeqCounters.get(ptKey) || 0) + 1
|
||||
playTimeSeqCounters.set(ptKey, nextSeq)
|
||||
|
||||
const newSchedule = {
|
||||
id: baseId + idx,
|
||||
song: song,
|
||||
playDate: selectedDate.value,
|
||||
sequence: localScheduledSongs.value.length + 1,
|
||||
playTimeId,
|
||||
sequence: nextSeq,
|
||||
playTimeId: targetPlayTimeId,
|
||||
isNew: true,
|
||||
isLocalOnly: true
|
||||
}
|
||||
@@ -2094,6 +2113,18 @@ const autoScheduleExpected = () => {
|
||||
count++
|
||||
})
|
||||
|
||||
const ptOrder = (id) => {
|
||||
if (id == null || id === '') return 1e9
|
||||
const pt = (playTimes.value || []).find(p => p.id === id)
|
||||
if (!pt || !pt.startTime) return 1e8
|
||||
const [h, m] = pt.startTime.split(':').map(Number)
|
||||
return h * 60 + (m || 0)
|
||||
}
|
||||
localScheduledSongs.value.sort((a, b) => {
|
||||
const po = ptOrder(a.playTimeId) - ptOrder(b.playTimeId)
|
||||
if (po !== 0) return po
|
||||
return (a.sequence || 0) - (b.sequence || 0)
|
||||
})
|
||||
localScheduledSongs.value.forEach((item, idx) => {
|
||||
item.sequence = idx + 1
|
||||
})
|
||||
@@ -2101,7 +2132,9 @@ const autoScheduleExpected = () => {
|
||||
hasChanges.value = true
|
||||
const strategyLabel =
|
||||
autoScheduleStrategy.value === 'contributor-count' ? '(按投稿人数优先)' : ''
|
||||
showToast(`已自动排入 ${count} 首待排歌曲${strategyLabel},请注意调整顺序后保存`, 'success')
|
||||
const byPref = expectedSongs.filter(s => s.preferredPlayTimeId).length
|
||||
const prefInfo = byPref ? `,其中 ${byPref} 首按期望播出时段排入` : ''
|
||||
showToast(`已自动排入 ${count} 首待排歌曲${strategyLabel}${prefInfo},请注意调整顺序后保存`, 'success')
|
||||
}
|
||||
|
||||
// 构建播放队列
|
||||
|
||||
Reference in New Issue
Block a user