diff --git a/src/i18n/lang/en-US/common.ts b/src/i18n/lang/en-US/common.ts index 98b4c9e..4802692 100644 --- a/src/i18n/lang/en-US/common.ts +++ b/src/i18n/lang/en-US/common.ts @@ -27,6 +27,7 @@ export default { refresh: 'Refresh', retry: 'Retry', reset: 'Reset', + back: 'Back', copySuccess: 'Copied to clipboard', copyFailed: 'Copy failed', validation: { diff --git a/src/i18n/lang/en-US/comp.ts b/src/i18n/lang/en-US/comp.ts index 6434031..ef03a28 100644 --- a/src/i18n/lang/en-US/comp.ts +++ b/src/i18n/lang/en-US/comp.ts @@ -120,5 +120,65 @@ export default { addToPlaylistSuccess: 'Add to Playlist Success', operationFailed: 'Operation Failed', songsAlreadyInPlaylist: 'Songs already in playlist' + }, + playlist: { + import: { + button: 'Import Playlist', + title: 'Import Playlist', + description: 'Import playlists via metadata, text, or links', + linkTab: 'Import by Link', + textTab: 'Import by Text', + localTab: 'Import by Metadata', + linkPlaceholder: 'Enter playlist links, one per line', + textPlaceholder: 'Enter song information in format: Song Name Artist Name', + localPlaceholder: 'Enter song metadata in JSON format', + linkTips: 'Supported link sources:', + linkTip1: 'Copy links after sharing playlists to WeChat/Weibo/QQ', + linkTip2: 'Directly copy playlist/profile links', + linkTip3: 'Directly copy article links', + textTips: 'Enter song information, one song per line', + textFormat: 'Format: Song Name Artist Name', + localTips: 'Add song metadata', + localFormat: 'Format example:', + songNamePlaceholder: 'Song Name', + artistNamePlaceholder: 'Artist Name', + albumNamePlaceholder: 'Album Name', + addSongButton: 'Add Song', + addLinkButton: 'Add Link', + importToStarPlaylist: 'Import to My Favorite Music', + playlistNamePlaceholder: 'Enter playlist name', + importButton: 'Start Import', + emptyLinkWarning: 'Please enter playlist links', + emptyTextWarning: 'Please enter song information', + emptyLocalWarning: 'Please enter song metadata', + invalidJsonFormat: 'Invalid JSON format', + importSuccess: 'Import task created successfully', + importFailed: 'Import failed', + importStatus: 'Import Status', + refresh: 'Refresh', + taskId: 'Task ID', + status: 'Status', + successCount: 'Success Count', + failReason: 'Failure Reason', + unknownError: 'Unknown error', + statusPending: 'Pending', + statusProcessing: 'Processing', + statusSuccess: 'Success', + statusFailed: 'Failed', + statusUnknown: 'Unknown', + taskList: 'Task List', + taskListTitle: 'Import Task List', + action: 'Action', + select: 'Select', + fetchTaskListFailed: 'Failed to fetch task list', + noTasks: 'No import tasks', + clearTasks: 'Clear Tasks', + clearTasksConfirmTitle: 'Confirm Clear', + clearTasksConfirmContent: 'Are you sure you want to clear all import task records? This action cannot be undone.', + confirm: 'Confirm', + cancel: 'Cancel', + clearTasksSuccess: 'Task list cleared', + clearTasksFailed: 'Failed to clear task list' + } } }; diff --git a/src/i18n/lang/zh-CN/common.ts b/src/i18n/lang/zh-CN/common.ts index 6b3fe42..746a97f 100644 --- a/src/i18n/lang/zh-CN/common.ts +++ b/src/i18n/lang/zh-CN/common.ts @@ -27,6 +27,7 @@ export default { refresh: '刷新', retry: '重试', reset: '重置', + back: '返回', copySuccess: '已复制到剪贴板', copyFailed: '复制失败', validation: { diff --git a/src/i18n/lang/zh-CN/comp.ts b/src/i18n/lang/zh-CN/comp.ts index f494fc6..2e666ef 100644 --- a/src/i18n/lang/zh-CN/comp.ts +++ b/src/i18n/lang/zh-CN/comp.ts @@ -118,5 +118,65 @@ export default { addToPlaylist: '添加到播放列表', addToPlaylistSuccess: '添加到播放列表成功', songsAlreadyInPlaylist: '歌曲已存在于播放列表中' + }, + playlist: { + import: { + button: '歌单导入', + title: '歌单导入', + description: '支持通过元数据/文字/链接三种方式导入歌单', + linkTab: '链接导入', + textTab: '文字导入', + localTab: '元数据导入', + linkPlaceholder: '请输入歌单链接,每行一个', + textPlaceholder: '请输入歌曲信息,格式为:歌曲名 歌手名', + localPlaceholder: '请输入JSON格式的歌曲元数据', + linkTips: '支持的链接来源:', + linkTip1: '将歌单分享到微信/微博/QQ后复制链接', + linkTip2: '直接复制歌单/个人主页链接', + linkTip3: '直接复制文章链接', + textTips: '请输入歌曲信息,每行一首歌', + textFormat: '格式:歌曲名 歌手名', + localTips: '请添加歌曲元数据', + localFormat: '格式示例:', + songNamePlaceholder: '歌曲名称', + artistNamePlaceholder: '艺术家名称', + albumNamePlaceholder: '专辑名称', + addSongButton: '添加歌曲', + addLinkButton: '添加链接', + importToStarPlaylist: '导入到我喜欢的音乐', + playlistNamePlaceholder: '请输入歌单名称', + importButton: '开始导入', + emptyLinkWarning: '请输入歌单链接', + emptyTextWarning: '请输入歌曲信息', + emptyLocalWarning: '请输入歌曲元数据', + invalidJsonFormat: 'JSON格式不正确', + importSuccess: '导入任务创建成功', + importFailed: '导入失败', + importStatus: '导入状态', + refresh: '刷新', + taskId: '任务ID', + status: '状态', + successCount: '成功数量', + failReason: '失败原因', + unknownError: '未知错误', + statusPending: '等待处理', + statusProcessing: '处理中', + statusSuccess: '导入成功', + statusFailed: '导入失败', + statusUnknown: '未知状态', + taskList: '任务列表', + taskListTitle: '导入任务列表', + action: '操作', + select: '选择', + fetchTaskListFailed: '获取任务列表失败', + noTasks: '暂无导入任务', + clearTasks: '清除任务', + clearTasksConfirmTitle: '确认清除', + clearTasksConfirmContent: '确定要清除所有导入任务记录吗?此操作不可恢复。', + confirm: '确认', + cancel: '取消', + clearTasksSuccess: '任务列表已清除', + clearTasksFailed: '清除任务列表失败' + } } }; diff --git a/src/renderer/api/playlist.ts b/src/renderer/api/playlist.ts new file mode 100644 index 0000000..9de168b --- /dev/null +++ b/src/renderer/api/playlist.ts @@ -0,0 +1,27 @@ +import request from '@/utils/request'; + +/** + * 歌单导入 - 元数据/文字/链接导入 + * @param params 导入参数 + */ +export function importPlaylist(params: { + local?: string; + text?: string; + link?: string; + importStarPlaylist?: boolean; + playlistName?: string; +}) { + return request.post('/playlist/import/name/task/create', params); +} + +/** + * 歌单导入 - 任务状态 + * @param id 任务ID + */ +export function getImportTaskStatus(id: string | number) { + return request({ + url: '/playlist/import/task/status', + method: 'get', + params: { id } + }); +} \ No newline at end of file diff --git a/src/renderer/components.d.ts b/src/renderer/components.d.ts index 0c4f1b1..c1c31c1 100644 --- a/src/renderer/components.d.ts +++ b/src/renderer/components.d.ts @@ -13,6 +13,7 @@ declare module 'vue' { NBadge: typeof import('naive-ui')['NBadge'] NButton: typeof import('naive-ui')['NButton'] NButtonGroup: typeof import('naive-ui')['NButtonGroup'] + NCard: typeof import('naive-ui')['NCard'] NCarousel: typeof import('naive-ui')['NCarousel'] NCarouselItem: typeof import('naive-ui')['NCarouselItem'] NCheckbox: typeof import('naive-ui')['NCheckbox'] diff --git a/src/renderer/router/other.ts b/src/renderer/router/other.ts index 81084e3..9b6487c 100644 --- a/src/renderer/router/other.ts +++ b/src/renderer/router/other.ts @@ -76,6 +76,16 @@ const otherRouter = [ back: true }, component: () => import('@/views/music/MusicListPage.vue') - } + }, + { + path: '/playlist/import', + name: 'playlistImport', + meta: { + title: '歌单导入', + keepAlive: true, + back: true + }, + component: () => import('@/views/playlist/ImportPlaylist.vue') + }, ]; export default otherRouter; diff --git a/src/renderer/views/playlist/ImportPlaylist.vue b/src/renderer/views/playlist/ImportPlaylist.vue new file mode 100644 index 0000000..14aca92 --- /dev/null +++ b/src/renderer/views/playlist/ImportPlaylist.vue @@ -0,0 +1,627 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/views/user/index.vue b/src/renderer/views/user/index.vue index b0ea052..36db8d5 100644 --- a/src/renderer/views/user/index.vue +++ b/src/renderer/views/user/index.vue @@ -28,7 +28,10 @@
{{ userDetail.profile.signature }}
-
{{ t('user.playlist.created') }}
+
+
{{ t('user.playlist.created') }}
+
{{ t('comp.playlist.import.button') }}
+
userStore.user); +const goToImportPlaylist = () => { + router.push('/playlist/import'); +}; + onBeforeUnmount(() => { mounted.value = false; }); @@ -278,8 +285,13 @@ const showFollowList = () => { @apply bg-black bg-opacity-40; } .title { - @apply text-lg font-bold; + @apply text-lg font-bold flex items-center justify-between; @apply text-gray-900 dark:text-white; + .import-btn { + @apply bg-light-100 font-normal rounded-lg px-2 py-1 text-opacity-70 text-sm hover:bg-light-200 hover:text-green-500 dark:bg-dark-200 dark:hover:bg-dark-300 dark:hover:text-green-400; + @apply cursor-pointer; + @apply transition-all duration-200; + } } .user-name {