[O] Decouple
This commit is contained in:
@@ -12,3 +12,5 @@ Practice Japanese Karaoke lyrics reading and typing at the same time with KashiD
|
|||||||
4. 历史成绩和进步曲线
|
4. 历史成绩和进步曲线
|
||||||
5. 纯前端存储数据
|
5. 纯前端存储数据
|
||||||
|
|
||||||
|
TODO: 404 page
|
||||||
|
TODO: Update an existing playlist
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { LinearProgress, TextFieldOutlined } from "m3-svelte"
|
import { TextFieldOutlined } from "m3-svelte"
|
||||||
import AppBar from "../../../components/appbar/AppBar.svelte"
|
import AppBar from "../../../components/appbar/AppBar.svelte"
|
||||||
import Button from "../../../components/Button.svelte"
|
import Button from "../../../components/Button.svelte"
|
||||||
import type { NeteaseSong } from "../../../shared/types"
|
import type { NeteaseSong } from "../../../shared/types"
|
||||||
import { API } from "../../../lib/client"
|
import { API } from "../../../lib/client"
|
||||||
import ErrorDialog from "../../../components/status/ErrorDialog.svelte";
|
import ErrorDialog from "../../../components/status/ErrorDialog.svelte";
|
||||||
|
import ProgressList from "./ProgressList.svelte";
|
||||||
|
|
||||||
let link = $state('')
|
let link = $state('')
|
||||||
|
|
||||||
@@ -15,12 +16,13 @@
|
|||||||
|
|
||||||
let status = $state<'idle' | 'importing' | 'success' | 'error'>('idle')
|
let status = $state<'idle' | 'importing' | 'success' | 'error'>('idle')
|
||||||
let songs = $state<SongImportStatus[]>([])
|
let songs = $state<SongImportStatus[]>([])
|
||||||
|
let error = $state('')
|
||||||
|
let id = $state('')
|
||||||
|
|
||||||
let progress = $derived({
|
let progress = $derived({
|
||||||
total: songs.length,
|
total: songs.length,
|
||||||
done: songs.filter(song => song.status !== 'importing').length
|
done: songs.filter(song => song.status !== 'importing').length
|
||||||
})
|
})
|
||||||
let error = $state('')
|
|
||||||
let id = $state('')
|
|
||||||
|
|
||||||
function statusToIcon(stat: string): string {
|
function statusToIcon(stat: string): string {
|
||||||
switch (stat) {
|
switch (stat) {
|
||||||
@@ -32,6 +34,15 @@
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let listTitle = $derived(status === 'idle' ? '' : (status === 'importing' ? '正在导入' : (status === 'success' ? '导入完成' : '导入出错')));
|
||||||
|
let listSubtitle = $derived(`${progress.done} / ${progress.total} 首歌曲`);
|
||||||
|
let listPercent = $derived(progress.total ? progress.done / progress.total * 100 : 0);
|
||||||
|
let listItems = $derived(songs.map(song => ({
|
||||||
|
title: song.song.name,
|
||||||
|
subtitle: song.song.ar.map(a => a.name).join(', '),
|
||||||
|
icon: statusToIcon(song.status)
|
||||||
|
})));
|
||||||
|
|
||||||
async function startImport() {
|
async function startImport() {
|
||||||
if (!link) return
|
if (!link) return
|
||||||
status = 'importing'
|
status = 'importing'
|
||||||
@@ -73,29 +84,7 @@
|
|||||||
<TextFieldOutlined label="网易云歌单链接 / ID" bind:value={link} />
|
<TextFieldOutlined label="网易云歌单链接 / ID" bind:value={link} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if status !== 'idle'}
|
<ProgressList title={listTitle} subtitle={listSubtitle} percentage={listPercent} items={listItems} />
|
||||||
<div class="hbox gap-12px items-end! h-48px p-content">
|
|
||||||
<div class="m3-font-headline-small">
|
|
||||||
{#if status === 'importing'}正在导入{:else if status === 'success'}导入完成{:else}导入出错{/if}
|
|
||||||
</div>
|
|
||||||
<div class="m3-font-label-small pb-3px">{progress.done} / {progress.total} 首歌曲</div>
|
|
||||||
</div>
|
|
||||||
<LinearProgress percent={progress.total ? progress.done / progress.total * 100 : 0}/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="vbox p-content scroll-here gap-8px">
|
|
||||||
{#if status !== 'idle'}
|
|
||||||
{#each songs as song}
|
|
||||||
<div class="hbox gap-12px items-center">
|
|
||||||
<span class="{statusToIcon(song.status)} text-xl"></span>
|
|
||||||
<div class="vbox">
|
|
||||||
<span class="m3-font-title-medium">{song.song.name}</span>
|
|
||||||
<span class="m3-font-body-small mfg-on-surface-variant">{song.song.ar.map(a => a.name).join(', ')}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="py-16px p-content">
|
<div class="py-16px p-content">
|
||||||
{#if status === 'idle'}
|
{#if status === 'idle'}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { LinearProgress } from "m3-svelte"
|
||||||
|
|
||||||
|
export interface ProgressItem {
|
||||||
|
title: string
|
||||||
|
subtitle?: string
|
||||||
|
icon: string
|
||||||
|
}
|
||||||
|
|
||||||
|
let { title, subtitle, percentage, items } = $props<{
|
||||||
|
title?: string,
|
||||||
|
subtitle?: string,
|
||||||
|
percentage?: number,
|
||||||
|
items: ProgressItem[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-1 vbox gap-16px min-h-0">
|
||||||
|
{#if title}
|
||||||
|
<div class="hbox gap-12px items-end! h-48px p-content">
|
||||||
|
<div class="m3-font-headline-small">
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
<div class="m3-font-label-small pb-3px">{subtitle}</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<LinearProgress percent={percentage ?? 0}/>
|
||||||
|
|
||||||
|
<div class="vbox p-content scroll-here gap-8px">
|
||||||
|
{#each items as item}
|
||||||
|
<div class="hbox gap-12px items-center h-40px">
|
||||||
|
<span class="{item.icon} text-xl"></span>
|
||||||
|
<div class="vbox">
|
||||||
|
<span class="m3-font-title-medium">{item.title}</span>
|
||||||
|
{#if item.subtitle}
|
||||||
|
<span class="m3-font-body-small mfg-on-surface-variant">{item.subtitle}</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user