mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-24 19:13:00 +08:00
✨ feat: 切换虚拟列表组件
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
out
|
||||
.gitignore
|
||||
auto-imports.d.ts
|
||||
components.d.ts
|
||||
@@ -3,11 +3,14 @@
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"DirectiveBinding": true,
|
||||
"EffectScope": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"MaybeRef": true,
|
||||
"MaybeRefOrGetter": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"VNode": true,
|
||||
@@ -71,6 +74,7 @@
|
||||
"onStartTyping": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"onWatcherCleanup": true,
|
||||
"pausableWatch": true,
|
||||
"provide": true,
|
||||
"provideLocal": true,
|
||||
@@ -180,6 +184,7 @@
|
||||
"useFullscreen": true,
|
||||
"useGamepad": true,
|
||||
"useGeolocation": true,
|
||||
"useId": true,
|
||||
"useIdle": true,
|
||||
"useImage": true,
|
||||
"useInfiniteScroll": true,
|
||||
@@ -198,6 +203,7 @@
|
||||
"useMemoize": true,
|
||||
"useMemory": true,
|
||||
"useMessage": true,
|
||||
"useModel": true,
|
||||
"useMounted": true,
|
||||
"useMouse": true,
|
||||
"useMouseInElement": true,
|
||||
@@ -246,6 +252,7 @@
|
||||
"useStyleTag": true,
|
||||
"useSupported": true,
|
||||
"useSwipe": true,
|
||||
"useTemplateRef": true,
|
||||
"useTemplateRefsList": true,
|
||||
"useTextDirection": true,
|
||||
"useTextSelection": true,
|
||||
@@ -292,13 +299,6 @@
|
||||
"watchThrottled": true,
|
||||
"watchTriggerable": true,
|
||||
"watchWithFilter": true,
|
||||
"whenever": true,
|
||||
"DirectiveBinding": true,
|
||||
"MaybeRef": true,
|
||||
"MaybeRefOrGetter": true,
|
||||
"onWatcherCleanup": true,
|
||||
"useId": true,
|
||||
"useModel": true,
|
||||
"useTemplateRef": true
|
||||
"whenever": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:vue/vue3-essential",
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
files: [".eslintrc.{js,cjs}"],
|
||||
parserOptions: {
|
||||
sourceType: "script",
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
parser: "@typescript-eslint/parser",
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: ["@typescript-eslint", "vue"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"vue/multi-word-component-names": "off",
|
||||
},
|
||||
globals: {
|
||||
h: "readonly",
|
||||
ref: "readonly",
|
||||
computed: "readonly",
|
||||
watch: "readonly",
|
||||
watchEffect: "readonly",
|
||||
onBeforeMount: "readonly",
|
||||
onBeforeUnmount: "readonly",
|
||||
onBeforeUpdate: "readonly",
|
||||
reactive: "readonly",
|
||||
onMounted: "readonly",
|
||||
onUnmounted: "readonly",
|
||||
onActivated: "readonly",
|
||||
onDeactivated: "readonly",
|
||||
onRenderTracked: "readonly",
|
||||
onRenderTriggered: "readonly",
|
||||
onServerPrefetch: "readonly",
|
||||
},
|
||||
};
|
||||
304
auto-eslint.mjs
Normal file
304
auto-eslint.mjs
Normal file
@@ -0,0 +1,304 @@
|
||||
export default {
|
||||
"globals": {
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"DirectiveBinding": true,
|
||||
"EffectScope": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"MaybeRef": true,
|
||||
"MaybeRefOrGetter": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"asyncComputed": true,
|
||||
"autoResetRef": true,
|
||||
"computed": true,
|
||||
"computedAsync": true,
|
||||
"computedEager": true,
|
||||
"computedInject": true,
|
||||
"computedWithControl": true,
|
||||
"controlledComputed": true,
|
||||
"controlledRef": true,
|
||||
"createApp": true,
|
||||
"createEventHook": true,
|
||||
"createGlobalState": true,
|
||||
"createInjectionState": true,
|
||||
"createReactiveFn": true,
|
||||
"createReusableTemplate": true,
|
||||
"createSharedComposable": true,
|
||||
"createTemplatePromise": true,
|
||||
"createUnrefFn": true,
|
||||
"customRef": true,
|
||||
"debouncedRef": true,
|
||||
"debouncedWatch": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"eagerComputed": true,
|
||||
"effectScope": true,
|
||||
"extendRef": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"h": true,
|
||||
"ignorableWatch": true,
|
||||
"inject": true,
|
||||
"injectLocal": true,
|
||||
"isDefined": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"makeDestructurable": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeRouteLeave": true,
|
||||
"onBeforeRouteUpdate": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onClickOutside": true,
|
||||
"onDeactivated": true,
|
||||
"onErrorCaptured": true,
|
||||
"onKeyStroke": true,
|
||||
"onLongPress": true,
|
||||
"onMounted": true,
|
||||
"onRenderTracked": true,
|
||||
"onRenderTriggered": true,
|
||||
"onScopeDispose": true,
|
||||
"onServerPrefetch": true,
|
||||
"onStartTyping": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"onWatcherCleanup": true,
|
||||
"pausableWatch": true,
|
||||
"provide": true,
|
||||
"provideLocal": true,
|
||||
"reactify": true,
|
||||
"reactifyObject": true,
|
||||
"reactive": true,
|
||||
"reactiveComputed": true,
|
||||
"reactiveOmit": true,
|
||||
"reactivePick": true,
|
||||
"readonly": true,
|
||||
"ref": true,
|
||||
"refAutoReset": true,
|
||||
"refDebounced": true,
|
||||
"refDefault": true,
|
||||
"refThrottled": true,
|
||||
"refWithControl": true,
|
||||
"resolveComponent": true,
|
||||
"resolveRef": true,
|
||||
"resolveUnref": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"syncRef": true,
|
||||
"syncRefs": true,
|
||||
"templateRef": true,
|
||||
"throttledRef": true,
|
||||
"throttledWatch": true,
|
||||
"toRaw": true,
|
||||
"toReactive": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"toValue": true,
|
||||
"triggerRef": true,
|
||||
"tryOnBeforeMount": true,
|
||||
"tryOnBeforeUnmount": true,
|
||||
"tryOnMounted": true,
|
||||
"tryOnScopeDispose": true,
|
||||
"tryOnUnmounted": true,
|
||||
"unref": true,
|
||||
"unrefElement": true,
|
||||
"until": true,
|
||||
"useActiveElement": true,
|
||||
"useAnimate": true,
|
||||
"useArrayDifference": true,
|
||||
"useArrayEvery": true,
|
||||
"useArrayFilter": true,
|
||||
"useArrayFind": true,
|
||||
"useArrayFindIndex": true,
|
||||
"useArrayFindLast": true,
|
||||
"useArrayIncludes": true,
|
||||
"useArrayJoin": true,
|
||||
"useArrayMap": true,
|
||||
"useArrayReduce": true,
|
||||
"useArraySome": true,
|
||||
"useArrayUnique": true,
|
||||
"useAsyncQueue": true,
|
||||
"useAsyncState": true,
|
||||
"useAttrs": true,
|
||||
"useBase64": true,
|
||||
"useBattery": true,
|
||||
"useBluetooth": true,
|
||||
"useBreakpoints": true,
|
||||
"useBroadcastChannel": true,
|
||||
"useBrowserLocation": true,
|
||||
"useCached": true,
|
||||
"useClipboard": true,
|
||||
"useClipboardItems": true,
|
||||
"useCloned": true,
|
||||
"useColorMode": true,
|
||||
"useConfirmDialog": true,
|
||||
"useCounter": true,
|
||||
"useCssModule": true,
|
||||
"useCssVar": true,
|
||||
"useCssVars": true,
|
||||
"useCurrentElement": true,
|
||||
"useCycleList": true,
|
||||
"useDark": true,
|
||||
"useDateFormat": true,
|
||||
"useDebounce": true,
|
||||
"useDebounceFn": true,
|
||||
"useDebouncedRefHistory": true,
|
||||
"useDeviceMotion": true,
|
||||
"useDeviceOrientation": true,
|
||||
"useDevicePixelRatio": true,
|
||||
"useDevicesList": true,
|
||||
"useDialog": true,
|
||||
"useDisplayMedia": true,
|
||||
"useDocumentVisibility": true,
|
||||
"useDraggable": true,
|
||||
"useDropZone": true,
|
||||
"useElementBounding": true,
|
||||
"useElementByPoint": true,
|
||||
"useElementHover": true,
|
||||
"useElementSize": true,
|
||||
"useElementVisibility": true,
|
||||
"useEventBus": true,
|
||||
"useEventListener": true,
|
||||
"useEventSource": true,
|
||||
"useEyeDropper": true,
|
||||
"useFavicon": true,
|
||||
"useFetch": true,
|
||||
"useFileDialog": true,
|
||||
"useFileSystemAccess": true,
|
||||
"useFocus": true,
|
||||
"useFocusWithin": true,
|
||||
"useFps": true,
|
||||
"useFullscreen": true,
|
||||
"useGamepad": true,
|
||||
"useGeolocation": true,
|
||||
"useId": true,
|
||||
"useIdle": true,
|
||||
"useImage": true,
|
||||
"useInfiniteScroll": true,
|
||||
"useIntersectionObserver": true,
|
||||
"useInterval": true,
|
||||
"useIntervalFn": true,
|
||||
"useKeyModifier": true,
|
||||
"useLastChanged": true,
|
||||
"useLink": true,
|
||||
"useLoadingBar": true,
|
||||
"useLocalStorage": true,
|
||||
"useMagicKeys": true,
|
||||
"useManualRefHistory": true,
|
||||
"useMediaControls": true,
|
||||
"useMediaQuery": true,
|
||||
"useMemoize": true,
|
||||
"useMemory": true,
|
||||
"useMessage": true,
|
||||
"useModel": true,
|
||||
"useMounted": true,
|
||||
"useMouse": true,
|
||||
"useMouseInElement": true,
|
||||
"useMousePressed": true,
|
||||
"useMutationObserver": true,
|
||||
"useNavigatorLanguage": true,
|
||||
"useNetwork": true,
|
||||
"useNotification": true,
|
||||
"useNow": true,
|
||||
"useObjectUrl": true,
|
||||
"useOffsetPagination": true,
|
||||
"useOnline": true,
|
||||
"usePageLeave": true,
|
||||
"useParallax": true,
|
||||
"useParentElement": true,
|
||||
"usePerformanceObserver": true,
|
||||
"usePermission": true,
|
||||
"usePointer": true,
|
||||
"usePointerLock": true,
|
||||
"usePointerSwipe": true,
|
||||
"usePreferredColorScheme": true,
|
||||
"usePreferredContrast": true,
|
||||
"usePreferredDark": true,
|
||||
"usePreferredLanguages": true,
|
||||
"usePreferredReducedMotion": true,
|
||||
"usePrevious": true,
|
||||
"useRafFn": true,
|
||||
"useRefHistory": true,
|
||||
"useResizeObserver": true,
|
||||
"useRoute": true,
|
||||
"useRouter": true,
|
||||
"useScreenOrientation": true,
|
||||
"useScreenSafeArea": true,
|
||||
"useScriptTag": true,
|
||||
"useScroll": true,
|
||||
"useScrollLock": true,
|
||||
"useSessionStorage": true,
|
||||
"useShare": true,
|
||||
"useSlots": true,
|
||||
"useSorted": true,
|
||||
"useSpeechRecognition": true,
|
||||
"useSpeechSynthesis": true,
|
||||
"useStepper": true,
|
||||
"useStorage": true,
|
||||
"useStorageAsync": true,
|
||||
"useStyleTag": true,
|
||||
"useSupported": true,
|
||||
"useSwipe": true,
|
||||
"useTemplateRef": true,
|
||||
"useTemplateRefsList": true,
|
||||
"useTextDirection": true,
|
||||
"useTextSelection": true,
|
||||
"useTextareaAutosize": true,
|
||||
"useThrottle": true,
|
||||
"useThrottleFn": true,
|
||||
"useThrottledRefHistory": true,
|
||||
"useTimeAgo": true,
|
||||
"useTimeout": true,
|
||||
"useTimeoutFn": true,
|
||||
"useTimeoutPoll": true,
|
||||
"useTimestamp": true,
|
||||
"useTitle": true,
|
||||
"useToNumber": true,
|
||||
"useToString": true,
|
||||
"useToggle": true,
|
||||
"useTransition": true,
|
||||
"useUrlSearchParams": true,
|
||||
"useUserMedia": true,
|
||||
"useVModel": true,
|
||||
"useVModels": true,
|
||||
"useVibrate": true,
|
||||
"useVirtualList": true,
|
||||
"useWakeLock": true,
|
||||
"useWebNotification": true,
|
||||
"useWebSocket": true,
|
||||
"useWebWorker": true,
|
||||
"useWebWorkerFn": true,
|
||||
"useWindowFocus": true,
|
||||
"useWindowScroll": true,
|
||||
"useWindowSize": true,
|
||||
"watch": true,
|
||||
"watchArray": true,
|
||||
"watchAtMost": true,
|
||||
"watchDebounced": true,
|
||||
"watchDeep": true,
|
||||
"watchEffect": true,
|
||||
"watchIgnorable": true,
|
||||
"watchImmediate": true,
|
||||
"watchOnce": true,
|
||||
"watchPausable": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true,
|
||||
"watchThrottled": true,
|
||||
"watchTriggerable": true,
|
||||
"watchWithFilter": true,
|
||||
"whenever": true
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ export default defineConfig(({ command, mode }) => {
|
||||
],
|
||||
eslintrc: {
|
||||
enabled: true,
|
||||
filepath: "./.eslintrc-auto-import.json",
|
||||
filepath: "./auto-eslint.mjs",
|
||||
},
|
||||
}),
|
||||
Components({
|
||||
|
||||
@@ -142,8 +142,10 @@ const initWinIpcMain = (
|
||||
|
||||
// 切换桌面歌词
|
||||
ipcMain.on("change-desktop-lyric", (_, val: boolean) => {
|
||||
val ? lyricWin?.show() : lyricWin?.hide();
|
||||
if (val) lyricWin?.setAlwaysOnTop(true, "screen-saver");
|
||||
if (val) {
|
||||
lyricWin?.show();
|
||||
lyricWin?.setAlwaysOnTop(true, "screen-saver");
|
||||
} else lyricWin?.hide();
|
||||
});
|
||||
|
||||
// 是否阻止系统息屏
|
||||
|
||||
69
eslint.config.mjs
Normal file
69
eslint.config.mjs
Normal file
@@ -0,0 +1,69 @@
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
import vue from "eslint-plugin-vue";
|
||||
import js from "@eslint/js";
|
||||
import globals from "globals";
|
||||
import path from "node:path";
|
||||
import autoEslint from "./auto-eslint.mjs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"**/node_modules",
|
||||
"**/dist",
|
||||
"**/out",
|
||||
"**/.gitignore",
|
||||
"**/auto-imports.d.ts",
|
||||
"**/components.d.ts",
|
||||
],
|
||||
},
|
||||
...compat.extends(
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:vue/vue3-essential",
|
||||
),
|
||||
{
|
||||
plugins: {
|
||||
"@typescript-eslint": typescriptEslint,
|
||||
vue,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
...autoEslint.globals,
|
||||
},
|
||||
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
|
||||
parserOptions: {
|
||||
parser: "@typescript-eslint/parser",
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"vue/multi-word-component-names": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/.eslintrc.{js,cjs}"],
|
||||
|
||||
languageOptions: {
|
||||
globals: { ...globals.node },
|
||||
ecmaVersion: 5,
|
||||
sourceType: "commonjs",
|
||||
},
|
||||
},
|
||||
];
|
||||
11
package.json
11
package.json
@@ -48,7 +48,7 @@
|
||||
"@pixi/filter-color-matrix": "^7.4.2",
|
||||
"@pixi/sprite": "^7.4.2",
|
||||
"@vueuse/core": "^10.11.1",
|
||||
"NeteaseCloudMusicApi": "^4.23.0",
|
||||
"NeteaseCloudMusicApi": "^4.23.1",
|
||||
"axios": "^1.7.7",
|
||||
"change-case": "^5.4.4",
|
||||
"dayjs": "^1.11.13",
|
||||
@@ -69,8 +69,7 @@
|
||||
"pinia": "^2.2.4",
|
||||
"pinia-plugin-persistedstate": "^3.2.3",
|
||||
"plyr": "^3.7.8",
|
||||
"vue-virt-list": "^1.5.2",
|
||||
"vue-virtual-scroller": "2.0.0-beta.8"
|
||||
"vue-virt-list": "^1.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/preload": "^3.0.1",
|
||||
@@ -84,8 +83,8 @@
|
||||
"@types/howler": "^2.2.12",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/node": "^22.7.4",
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^8.8.0",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"ajv": "^8.17.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
@@ -93,7 +92,7 @@
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-log": "^5.2.0",
|
||||
"electron-vite": "^2.3.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint": "^9.12.0",
|
||||
"eslint-plugin-vue": "^9.28.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fastify": "^4.28.1",
|
||||
|
||||
509
pnpm-lock.yaml
generated
509
pnpm-lock.yaml
generated
@@ -54,8 +54,8 @@ importers:
|
||||
specifier: ^10.11.1
|
||||
version: 10.11.1(vue@3.5.10(typescript@5.6.2))
|
||||
NeteaseCloudMusicApi:
|
||||
specifier: ^4.23.0
|
||||
version: 4.23.0
|
||||
specifier: ^4.23.1
|
||||
version: 4.23.1
|
||||
axios:
|
||||
specifier: ^1.7.7
|
||||
version: 1.7.7
|
||||
@@ -119,9 +119,6 @@ importers:
|
||||
vue-virt-list:
|
||||
specifier: ^1.5.2
|
||||
version: 1.5.2(vue@3.5.10(typescript@5.6.2))
|
||||
vue-virtual-scroller:
|
||||
specifier: 2.0.0-beta.8
|
||||
version: 2.0.0-beta.8(vue@3.5.10(typescript@5.6.2))
|
||||
devDependencies:
|
||||
'@electron-toolkit/tsconfig':
|
||||
specifier: ^1.0.1
|
||||
@@ -151,11 +148,11 @@ importers:
|
||||
specifier: ^22.7.4
|
||||
version: 22.7.4
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^7.18.0
|
||||
version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)
|
||||
specifier: ^8.8.0
|
||||
version: 8.8.0(@typescript-eslint/parser@8.8.0(eslint@9.12.0)(typescript@5.6.2))(eslint@9.12.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.18.0
|
||||
version: 7.18.0(eslint@8.57.1)(typescript@5.6.2)
|
||||
specifier: ^8.8.0
|
||||
version: 8.8.0(eslint@9.12.0)(typescript@5.6.2)
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^5.1.4
|
||||
version: 5.1.4(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4)(terser@5.34.1))(vue@3.5.10(typescript@5.6.2))
|
||||
@@ -178,11 +175,11 @@ importers:
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4)(terser@5.34.1))
|
||||
eslint:
|
||||
specifier: ^8.57.1
|
||||
version: 8.57.1
|
||||
specifier: ^9.12.0
|
||||
version: 9.12.0
|
||||
eslint-plugin-vue:
|
||||
specifier: ^9.28.0
|
||||
version: 9.28.0(eslint@8.57.1)
|
||||
version: 9.28.0(eslint@9.12.0)
|
||||
fast-glob:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
@@ -552,13 +549,29 @@ packages:
|
||||
resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
|
||||
'@eslint/eslintrc@2.1.4':
|
||||
resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
'@eslint/config-array@0.18.0':
|
||||
resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/js@8.57.1':
|
||||
resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
'@eslint/core@0.6.0':
|
||||
resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/eslintrc@3.1.0':
|
||||
resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/js@9.12.0':
|
||||
resolution: {integrity: sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/object-schema@2.1.4':
|
||||
resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/plugin-kit@0.2.0':
|
||||
resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@fastify/accept-negotiator@1.1.0':
|
||||
resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==}
|
||||
@@ -601,18 +614,21 @@ packages:
|
||||
'@fastify/static@7.0.4':
|
||||
resolution: {integrity: sha512-p2uKtaf8BMOZWLs6wu+Ihg7bWNBdjNgCwDza4MJtTqg+5ovKmcbgbR9Xs5/smZ1YISfzKOCNYmZV8LaCj+eJ1Q==}
|
||||
|
||||
'@humanwhocodes/config-array@0.13.0':
|
||||
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
deprecated: Use @eslint/config-array instead
|
||||
'@humanfs/core@0.19.0':
|
||||
resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
|
||||
'@humanfs/node@0.16.5':
|
||||
resolution: {integrity: sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
|
||||
'@humanwhocodes/module-importer@1.0.1':
|
||||
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
||||
engines: {node: '>=12.22'}
|
||||
|
||||
'@humanwhocodes/object-schema@2.0.3':
|
||||
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
|
||||
deprecated: Use @eslint/object-schema instead
|
||||
'@humanwhocodes/retry@0.3.1':
|
||||
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@imsyy/color-utils@1.0.2':
|
||||
resolution: {integrity: sha512-sLXkCpdd5L8s0mVNWLaTBNXK4RYLNbEnlZIMT/wml8GCDFYsLLJbpkRraQUlMwpw8SgdmJCCi8T5GCUcfySwRw==}
|
||||
@@ -894,6 +910,9 @@ packages:
|
||||
'@types/js-cookie@3.0.6':
|
||||
resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
|
||||
|
||||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
'@types/katex@0.16.7':
|
||||
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
|
||||
|
||||
@@ -933,66 +952,62 @@ packages:
|
||||
'@types/yauzl@2.10.3':
|
||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@7.18.0':
|
||||
resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
'@typescript-eslint/eslint-plugin@8.8.0':
|
||||
resolution: {integrity: sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^7.0.0
|
||||
eslint: ^8.56.0
|
||||
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/parser@7.18.0':
|
||||
resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
'@typescript-eslint/parser@8.8.0':
|
||||
resolution: {integrity: sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.56.0
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/scope-manager@7.18.0':
|
||||
resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
'@typescript-eslint/scope-manager@8.8.0':
|
||||
resolution: {integrity: sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/type-utils@7.18.0':
|
||||
resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.56.0
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/types@7.18.0':
|
||||
resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@7.18.0':
|
||||
resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
'@typescript-eslint/type-utils@8.8.0':
|
||||
resolution: {integrity: sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/utils@7.18.0':
|
||||
resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
'@typescript-eslint/types@8.8.0':
|
||||
resolution: {integrity: sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.8.0':
|
||||
resolution: {integrity: sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.56.0
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/visitor-keys@7.18.0':
|
||||
resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
'@typescript-eslint/utils@8.8.0':
|
||||
resolution: {integrity: sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
|
||||
'@ungap/structured-clone@1.2.0':
|
||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||
'@typescript-eslint/visitor-keys@8.8.0':
|
||||
resolution: {integrity: sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@vitejs/plugin-vue@5.1.4':
|
||||
resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==}
|
||||
@@ -1001,14 +1016,14 @@ packages:
|
||||
vite: ^5.0.0
|
||||
vue: ^3.2.25
|
||||
|
||||
'@volar/language-core@2.4.5':
|
||||
resolution: {integrity: sha512-F4tA0DCO5Q1F5mScHmca0umsi2ufKULAnMOVBfMsZdT4myhVl4WdKRwCaKcfOkIEuyrAVvtq1ESBdZ+rSyLVww==}
|
||||
'@volar/language-core@2.4.6':
|
||||
resolution: {integrity: sha512-FxUfxaB8sCqvY46YjyAAV6c3mMIq/NWQMVvJ+uS4yxr1KzOvyg61gAuOnNvgCvO4TZ7HcLExBEsWcDu4+K4E8A==}
|
||||
|
||||
'@volar/source-map@2.4.5':
|
||||
resolution: {integrity: sha512-varwD7RaKE2J/Z+Zu6j3mNNJbNT394qIxXwdvz/4ao/vxOfyClZpSDtLKkwWmecinkOVos5+PWkWraelfMLfpw==}
|
||||
'@volar/source-map@2.4.6':
|
||||
resolution: {integrity: sha512-Nsh7UW2ruK+uURIPzjJgF0YRGP5CX9nQHypA2OMqdM2FKy7rh+uv3XgPnWPw30JADbKvZ5HuBzG4gSbVDYVtiw==}
|
||||
|
||||
'@volar/typescript@2.4.5':
|
||||
resolution: {integrity: sha512-mcT1mHvLljAEtHviVcBuOyAwwMKz1ibXTi5uYtP/pf4XxoAzpdkQ+Br2IC0NPCvLCbjPZmbf3I0udndkfB1CDg==}
|
||||
'@volar/typescript@2.4.6':
|
||||
resolution: {integrity: sha512-NMIrA7y5OOqddL9VtngPWYmdQU03htNKFtAYidbYfWA0TOhyGVd9tfcP4TsLWQ+RBWDZCbBqsr8xzU0ZOxYTCQ==}
|
||||
|
||||
'@vue/compiler-core@3.5.10':
|
||||
resolution: {integrity: sha512-iXWlk+Cg/ag7gLvY0SfVucU8Kh2CjysYZjhhP70w9qI4MvSox4frrP+vDGvtQuzIcgD8+sxM6lZvCtdxGunTAA==}
|
||||
@@ -1075,8 +1090,8 @@ packages:
|
||||
resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
NeteaseCloudMusicApi@4.23.0:
|
||||
resolution: {integrity: sha512-nTvEnrfkTKNWEPCHF/qbNLAQVLb90PaPjdBuht/po6OpGTwX9sWm7RXsqMr7TGw10mkR94CIvx9Ecb+H8gV8yA==}
|
||||
NeteaseCloudMusicApi@4.23.1:
|
||||
resolution: {integrity: sha512-knjTB2g7CcogBG9yaxj37BoazBH8a5r0kJgFfLo1JJngdUhRapk0z20XOAtG8mrSoFd6yxW8fimFqcL5lbmwEw==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1188,10 +1203,6 @@ packages:
|
||||
array-flatten@1.1.1:
|
||||
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
|
||||
|
||||
array-union@2.1.0:
|
||||
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
asn1@0.2.6:
|
||||
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
|
||||
|
||||
@@ -1455,8 +1466,8 @@ packages:
|
||||
resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
confbox@0.1.7:
|
||||
resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==}
|
||||
confbox@0.1.8:
|
||||
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
|
||||
|
||||
config-file-ts@0.2.6:
|
||||
resolution: {integrity: sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==}
|
||||
@@ -1638,10 +1649,6 @@ packages:
|
||||
dir-compare@3.3.0:
|
||||
resolution: {integrity: sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
dmg-builder@24.13.3:
|
||||
resolution: {integrity: sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==}
|
||||
|
||||
@@ -1651,10 +1658,6 @@ packages:
|
||||
os: [darwin]
|
||||
hasBin: true
|
||||
|
||||
doctrine@3.0.0:
|
||||
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
dot-prop@6.0.1:
|
||||
resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1809,14 +1812,31 @@ packages:
|
||||
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
eslint-scope@8.1.0:
|
||||
resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
eslint-visitor-keys@3.4.3:
|
||||
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
eslint@8.57.1:
|
||||
resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
eslint-visitor-keys@4.1.0:
|
||||
resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
eslint@9.12.0:
|
||||
resolution: {integrity: sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
jiti: '*'
|
||||
peerDependenciesMeta:
|
||||
jiti:
|
||||
optional: true
|
||||
|
||||
espree@10.2.0:
|
||||
resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
espree@9.6.1:
|
||||
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
|
||||
@@ -1950,9 +1970,9 @@ packages:
|
||||
fd-slicer@1.1.0:
|
||||
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
|
||||
|
||||
file-entry-cache@6.0.1:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
file-entry-cache@8.0.0:
|
||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
file-saver@2.0.5:
|
||||
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
||||
@@ -1988,9 +2008,9 @@ packages:
|
||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
flat-cache@3.2.0:
|
||||
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
flat-cache@4.0.1:
|
||||
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
flatted@3.3.1:
|
||||
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
|
||||
@@ -2126,14 +2146,14 @@ packages:
|
||||
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
globals@14.0.0:
|
||||
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
globalthis@1.0.4:
|
||||
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
globby@11.1.0:
|
||||
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
gopd@1.0.1:
|
||||
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
||||
|
||||
@@ -2318,10 +2338,6 @@ packages:
|
||||
resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-path-inside@3.0.3:
|
||||
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-plain-obj@1.1.0:
|
||||
resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2682,16 +2698,13 @@ packages:
|
||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
mitt@2.1.0:
|
||||
resolution: {integrity: sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==}
|
||||
|
||||
mkdirp@1.0.4:
|
||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
mlly@1.7.1:
|
||||
resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==}
|
||||
mlly@1.7.2:
|
||||
resolution: {integrity: sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==}
|
||||
|
||||
modify-filename@1.1.0:
|
||||
resolution: {integrity: sha512-EickqnKq3kVVaZisYuCxhtKbZjInCuwgwZWyAmRIp1NTMhri7r3380/uqwrUHfaDiPzLVTuoNy4whX66bxPVog==}
|
||||
@@ -2902,10 +2915,6 @@ packages:
|
||||
path-to-regexp@0.1.10:
|
||||
resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==}
|
||||
|
||||
path-type@4.0.0:
|
||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
pathe@1.1.2:
|
||||
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
|
||||
|
||||
@@ -3149,11 +3158,6 @@ packages:
|
||||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
rimraf@3.0.2:
|
||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
|
||||
roarr@2.15.4:
|
||||
resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
|
||||
engines: {node: '>=8.0'}
|
||||
@@ -3277,10 +3281,6 @@ packages:
|
||||
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
slash@3.0.0:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
slice-ansi@3.0.0:
|
||||
resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -3699,16 +3699,6 @@ packages:
|
||||
peerDependencies:
|
||||
eslint: '>=6.0.0'
|
||||
|
||||
vue-observe-visibility@2.0.0-alpha.1:
|
||||
resolution: {integrity: sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue-resize@2.0.0-alpha.1:
|
||||
resolution: {integrity: sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue-router@4.4.5:
|
||||
resolution: {integrity: sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==}
|
||||
peerDependencies:
|
||||
@@ -3729,11 +3719,6 @@ packages:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue-virtual-scroller@2.0.0-beta.8:
|
||||
resolution: {integrity: sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==}
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
vue@3.5.10:
|
||||
resolution: {integrity: sha512-Vy2kmJwHPlouC/tSnIgXVg03SG+9wSqT1xu1Vehc+ChsXsRd7jLkKgMltVEFOzUdBr3uFwBCG+41LJtfAcBRng==}
|
||||
peerDependencies:
|
||||
@@ -4165,19 +4150,29 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@eslint-community/eslint-utils@4.4.0(eslint@8.57.1)':
|
||||
'@eslint-community/eslint-utils@4.4.0(eslint@9.12.0)':
|
||||
dependencies:
|
||||
eslint: 8.57.1
|
||||
eslint: 9.12.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.11.1': {}
|
||||
|
||||
'@eslint/eslintrc@2.1.4':
|
||||
'@eslint/config-array@0.18.0':
|
||||
dependencies:
|
||||
'@eslint/object-schema': 2.1.4
|
||||
debug: 4.3.7
|
||||
minimatch: 3.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@eslint/core@0.6.0': {}
|
||||
|
||||
'@eslint/eslintrc@3.1.0':
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
debug: 4.3.7
|
||||
espree: 9.6.1
|
||||
globals: 13.24.0
|
||||
espree: 10.2.0
|
||||
globals: 14.0.0
|
||||
ignore: 5.3.2
|
||||
import-fresh: 3.3.0
|
||||
js-yaml: 4.1.0
|
||||
@@ -4186,7 +4181,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@eslint/js@8.57.1': {}
|
||||
'@eslint/js@9.12.0': {}
|
||||
|
||||
'@eslint/object-schema@2.1.4': {}
|
||||
|
||||
'@eslint/plugin-kit@0.2.0':
|
||||
dependencies:
|
||||
levn: 0.4.1
|
||||
|
||||
'@fastify/accept-negotiator@1.1.0': {}
|
||||
|
||||
@@ -4261,17 +4262,16 @@ snapshots:
|
||||
fastq: 1.17.1
|
||||
glob: 10.4.5
|
||||
|
||||
'@humanwhocodes/config-array@0.13.0':
|
||||
'@humanfs/core@0.19.0': {}
|
||||
|
||||
'@humanfs/node@0.16.5':
|
||||
dependencies:
|
||||
'@humanwhocodes/object-schema': 2.0.3
|
||||
debug: 4.3.7
|
||||
minimatch: 3.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
'@humanfs/core': 0.19.0
|
||||
'@humanwhocodes/retry': 0.3.1
|
||||
|
||||
'@humanwhocodes/module-importer@1.0.1': {}
|
||||
|
||||
'@humanwhocodes/object-schema@2.0.3': {}
|
||||
'@humanwhocodes/retry@0.3.1': {}
|
||||
|
||||
'@imsyy/color-utils@1.0.2':
|
||||
dependencies:
|
||||
@@ -4522,6 +4522,8 @@ snapshots:
|
||||
|
||||
'@types/js-cookie@3.0.6': {}
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/katex@0.16.7': {}
|
||||
|
||||
'@types/keyv@3.1.4':
|
||||
@@ -4566,15 +4568,15 @@ snapshots:
|
||||
'@types/node': 22.7.4
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)':
|
||||
'@typescript-eslint/eslint-plugin@8.8.0(@typescript-eslint/parser@8.8.0(eslint@9.12.0)(typescript@5.6.2))(eslint@9.12.0)(typescript@5.6.2)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.11.1
|
||||
'@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.6.2)
|
||||
'@typescript-eslint/scope-manager': 7.18.0
|
||||
'@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.2)
|
||||
'@typescript-eslint/visitor-keys': 7.18.0
|
||||
eslint: 8.57.1
|
||||
'@typescript-eslint/parser': 8.8.0(eslint@9.12.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/scope-manager': 8.8.0
|
||||
'@typescript-eslint/type-utils': 8.8.0(eslint@9.12.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.8.0(eslint@9.12.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/visitor-keys': 8.8.0
|
||||
eslint: 9.12.0
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.3.2
|
||||
natural-compare: 1.4.0
|
||||
@@ -4584,44 +4586,44 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.2)':
|
||||
'@typescript-eslint/parser@8.8.0(eslint@9.12.0)(typescript@5.6.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 7.18.0
|
||||
'@typescript-eslint/types': 7.18.0
|
||||
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2)
|
||||
'@typescript-eslint/visitor-keys': 7.18.0
|
||||
'@typescript-eslint/scope-manager': 8.8.0
|
||||
'@typescript-eslint/types': 8.8.0
|
||||
'@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.2)
|
||||
'@typescript-eslint/visitor-keys': 8.8.0
|
||||
debug: 4.3.7
|
||||
eslint: 8.57.1
|
||||
eslint: 9.12.0
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/scope-manager@7.18.0':
|
||||
'@typescript-eslint/scope-manager@8.8.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.18.0
|
||||
'@typescript-eslint/visitor-keys': 7.18.0
|
||||
'@typescript-eslint/types': 8.8.0
|
||||
'@typescript-eslint/visitor-keys': 8.8.0
|
||||
|
||||
'@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.6.2)':
|
||||
'@typescript-eslint/type-utils@8.8.0(eslint@9.12.0)(typescript@5.6.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.2)
|
||||
'@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.8.0(eslint@9.12.0)(typescript@5.6.2)
|
||||
debug: 4.3.7
|
||||
eslint: 8.57.1
|
||||
ts-api-utils: 1.3.0(typescript@5.6.2)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/types@7.18.0': {}
|
||||
'@typescript-eslint/types@8.8.0': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.2)':
|
||||
'@typescript-eslint/typescript-estree@8.8.0(typescript@5.6.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.18.0
|
||||
'@typescript-eslint/visitor-keys': 7.18.0
|
||||
'@typescript-eslint/types': 8.8.0
|
||||
'@typescript-eslint/visitor-keys': 8.8.0
|
||||
debug: 4.3.7
|
||||
globby: 11.1.0
|
||||
fast-glob: 3.3.2
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.5
|
||||
semver: 7.6.3
|
||||
@@ -4631,38 +4633,36 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.6.2)':
|
||||
'@typescript-eslint/utils@8.8.0(eslint@9.12.0)(typescript@5.6.2)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
|
||||
'@typescript-eslint/scope-manager': 7.18.0
|
||||
'@typescript-eslint/types': 7.18.0
|
||||
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2)
|
||||
eslint: 8.57.1
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
|
||||
'@typescript-eslint/scope-manager': 8.8.0
|
||||
'@typescript-eslint/types': 8.8.0
|
||||
'@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.2)
|
||||
eslint: 9.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@typescript-eslint/visitor-keys@7.18.0':
|
||||
'@typescript-eslint/visitor-keys@8.8.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.18.0
|
||||
'@typescript-eslint/types': 8.8.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@ungap/structured-clone@1.2.0': {}
|
||||
|
||||
'@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4)(terser@5.34.1))(vue@3.5.10(typescript@5.6.2))':
|
||||
dependencies:
|
||||
vite: 5.4.8(@types/node@22.7.4)(sass@1.79.4)(terser@5.34.1)
|
||||
vue: 3.5.10(typescript@5.6.2)
|
||||
|
||||
'@volar/language-core@2.4.5':
|
||||
'@volar/language-core@2.4.6':
|
||||
dependencies:
|
||||
'@volar/source-map': 2.4.5
|
||||
'@volar/source-map': 2.4.6
|
||||
|
||||
'@volar/source-map@2.4.5': {}
|
||||
'@volar/source-map@2.4.6': {}
|
||||
|
||||
'@volar/typescript@2.4.5':
|
||||
'@volar/typescript@2.4.6':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.5
|
||||
'@volar/language-core': 2.4.6
|
||||
path-browserify: 1.0.1
|
||||
vscode-uri: 3.0.8
|
||||
|
||||
@@ -4718,7 +4718,7 @@ snapshots:
|
||||
|
||||
'@vue/language-core@2.1.6(typescript@5.6.2)':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.5
|
||||
'@volar/language-core': 2.4.6
|
||||
'@vue/compiler-dom': 3.5.11
|
||||
'@vue/compiler-vue2': 2.7.16
|
||||
'@vue/shared': 3.5.11
|
||||
@@ -4776,7 +4776,7 @@ snapshots:
|
||||
|
||||
'@xmldom/xmldom@0.8.10': {}
|
||||
|
||||
NeteaseCloudMusicApi@4.23.0:
|
||||
NeteaseCloudMusicApi@4.23.1:
|
||||
dependencies:
|
||||
axios: 1.7.7
|
||||
crypto-js: 4.2.0
|
||||
@@ -4945,8 +4945,6 @@ snapshots:
|
||||
|
||||
array-flatten@1.1.1: {}
|
||||
|
||||
array-union@2.1.0: {}
|
||||
|
||||
asn1@0.2.6:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
@@ -5256,7 +5254,7 @@ snapshots:
|
||||
pkg-up: 3.1.0
|
||||
semver: 7.6.3
|
||||
|
||||
confbox@0.1.7: {}
|
||||
confbox@0.1.8: {}
|
||||
|
||||
config-file-ts@0.2.6:
|
||||
dependencies:
|
||||
@@ -5412,10 +5410,6 @@ snapshots:
|
||||
buffer-equal: 1.0.1
|
||||
minimatch: 3.1.2
|
||||
|
||||
dir-glob@3.0.1:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
|
||||
dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3):
|
||||
dependencies:
|
||||
app-builder-lib: 24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
@@ -5442,10 +5436,6 @@ snapshots:
|
||||
verror: 1.10.1
|
||||
optional: true
|
||||
|
||||
doctrine@3.0.0:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
dot-prop@6.0.1:
|
||||
dependencies:
|
||||
is-obj: 2.0.0
|
||||
@@ -5629,16 +5619,16 @@ snapshots:
|
||||
optionalDependencies:
|
||||
source-map: 0.6.1
|
||||
|
||||
eslint-plugin-vue@9.28.0(eslint@8.57.1):
|
||||
eslint-plugin-vue@9.28.0(eslint@9.12.0):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
|
||||
eslint: 8.57.1
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
|
||||
eslint: 9.12.0
|
||||
globals: 13.24.0
|
||||
natural-compare: 1.4.0
|
||||
nth-check: 2.1.1
|
||||
postcss-selector-parser: 6.1.2
|
||||
semver: 7.6.3
|
||||
vue-eslint-parser: 9.4.3(eslint@8.57.1)
|
||||
vue-eslint-parser: 9.4.3(eslint@9.12.0)
|
||||
xml-name-validator: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -5648,51 +5638,61 @@ snapshots:
|
||||
esrecurse: 4.3.0
|
||||
estraverse: 5.3.0
|
||||
|
||||
eslint-scope@8.1.0:
|
||||
dependencies:
|
||||
esrecurse: 4.3.0
|
||||
estraverse: 5.3.0
|
||||
|
||||
eslint-visitor-keys@3.4.3: {}
|
||||
|
||||
eslint@8.57.1:
|
||||
eslint-visitor-keys@4.1.0: {}
|
||||
|
||||
eslint@9.12.0:
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
|
||||
'@eslint-community/regexpp': 4.11.1
|
||||
'@eslint/eslintrc': 2.1.4
|
||||
'@eslint/js': 8.57.1
|
||||
'@humanwhocodes/config-array': 0.13.0
|
||||
'@eslint/config-array': 0.18.0
|
||||
'@eslint/core': 0.6.0
|
||||
'@eslint/eslintrc': 3.1.0
|
||||
'@eslint/js': 9.12.0
|
||||
'@eslint/plugin-kit': 0.2.0
|
||||
'@humanfs/node': 0.16.5
|
||||
'@humanwhocodes/module-importer': 1.0.1
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
'@ungap/structured-clone': 1.2.0
|
||||
'@humanwhocodes/retry': 0.3.1
|
||||
'@types/estree': 1.0.6
|
||||
'@types/json-schema': 7.0.15
|
||||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.3
|
||||
debug: 4.3.7
|
||||
doctrine: 3.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 7.2.2
|
||||
eslint-visitor-keys: 3.4.3
|
||||
espree: 9.6.1
|
||||
eslint-scope: 8.1.0
|
||||
eslint-visitor-keys: 4.1.0
|
||||
espree: 10.2.0
|
||||
esquery: 1.6.0
|
||||
esutils: 2.0.3
|
||||
fast-deep-equal: 3.1.3
|
||||
file-entry-cache: 6.0.1
|
||||
file-entry-cache: 8.0.0
|
||||
find-up: 5.0.0
|
||||
glob-parent: 6.0.2
|
||||
globals: 13.24.0
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.3.2
|
||||
imurmurhash: 0.1.4
|
||||
is-glob: 4.0.3
|
||||
is-path-inside: 3.0.3
|
||||
js-yaml: 4.1.0
|
||||
json-stable-stringify-without-jsonify: 1.0.1
|
||||
levn: 0.4.1
|
||||
lodash.merge: 4.6.2
|
||||
minimatch: 3.1.2
|
||||
natural-compare: 1.4.0
|
||||
optionator: 0.9.4
|
||||
strip-ansi: 6.0.1
|
||||
text-table: 0.2.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
espree@10.2.0:
|
||||
dependencies:
|
||||
acorn: 8.12.1
|
||||
acorn-jsx: 5.3.2(acorn@8.12.1)
|
||||
eslint-visitor-keys: 4.1.0
|
||||
|
||||
espree@9.6.1:
|
||||
dependencies:
|
||||
acorn: 8.12.1
|
||||
@@ -5872,9 +5872,9 @@ snapshots:
|
||||
dependencies:
|
||||
pend: 1.2.0
|
||||
|
||||
file-entry-cache@6.0.1:
|
||||
file-entry-cache@8.0.0:
|
||||
dependencies:
|
||||
flat-cache: 3.2.0
|
||||
flat-cache: 4.0.1
|
||||
|
||||
file-saver@2.0.5: {}
|
||||
|
||||
@@ -5924,11 +5924,10 @@ snapshots:
|
||||
locate-path: 6.0.0
|
||||
path-exists: 4.0.0
|
||||
|
||||
flat-cache@3.2.0:
|
||||
flat-cache@4.0.1:
|
||||
dependencies:
|
||||
flatted: 3.3.1
|
||||
keyv: 4.5.4
|
||||
rimraf: 3.0.2
|
||||
|
||||
flatted@3.3.1: {}
|
||||
|
||||
@@ -6088,21 +6087,14 @@ snapshots:
|
||||
dependencies:
|
||||
type-fest: 0.20.2
|
||||
|
||||
globals@14.0.0: {}
|
||||
|
||||
globalthis@1.0.4:
|
||||
dependencies:
|
||||
define-properties: 1.2.1
|
||||
gopd: 1.0.1
|
||||
optional: true
|
||||
|
||||
globby@11.1.0:
|
||||
dependencies:
|
||||
array-union: 2.1.0
|
||||
dir-glob: 3.0.1
|
||||
fast-glob: 3.3.2
|
||||
ignore: 5.3.2
|
||||
merge2: 1.4.1
|
||||
slash: 3.0.0
|
||||
|
||||
gopd@1.0.1:
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.4
|
||||
@@ -6281,8 +6273,6 @@ snapshots:
|
||||
|
||||
is-obj@2.0.0: {}
|
||||
|
||||
is-path-inside@3.0.3: {}
|
||||
|
||||
is-plain-obj@1.1.0: {}
|
||||
|
||||
is-stream@1.1.0: {}
|
||||
@@ -6496,7 +6486,7 @@ snapshots:
|
||||
|
||||
local-pkg@0.5.0:
|
||||
dependencies:
|
||||
mlly: 1.7.1
|
||||
mlly: 1.7.2
|
||||
pkg-types: 1.2.0
|
||||
|
||||
localforage@1.10.0:
|
||||
@@ -6639,11 +6629,9 @@ snapshots:
|
||||
minipass: 3.3.6
|
||||
yallist: 4.0.0
|
||||
|
||||
mitt@2.1.0: {}
|
||||
|
||||
mkdirp@1.0.4: {}
|
||||
|
||||
mlly@1.7.1:
|
||||
mlly@1.7.2:
|
||||
dependencies:
|
||||
acorn: 8.12.1
|
||||
pathe: 1.1.2
|
||||
@@ -6859,8 +6847,6 @@ snapshots:
|
||||
|
||||
path-to-regexp@0.1.10: {}
|
||||
|
||||
path-type@4.0.0: {}
|
||||
|
||||
pathe@1.1.2: {}
|
||||
|
||||
peek-readable@4.1.0: {}
|
||||
@@ -6908,8 +6894,8 @@ snapshots:
|
||||
|
||||
pkg-types@1.2.0:
|
||||
dependencies:
|
||||
confbox: 0.1.7
|
||||
mlly: 1.7.1
|
||||
confbox: 0.1.8
|
||||
mlly: 1.7.2
|
||||
pathe: 1.1.2
|
||||
|
||||
pkg-up@3.1.0:
|
||||
@@ -7111,10 +7097,6 @@ snapshots:
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
rimraf@3.0.2:
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
|
||||
roarr@2.15.4:
|
||||
dependencies:
|
||||
boolean: 3.2.0
|
||||
@@ -7266,8 +7248,6 @@ snapshots:
|
||||
dependencies:
|
||||
semver: 7.6.3
|
||||
|
||||
slash@3.0.0: {}
|
||||
|
||||
slice-ansi@3.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
@@ -7516,7 +7496,7 @@ snapshots:
|
||||
fast-glob: 3.3.2
|
||||
local-pkg: 0.5.0
|
||||
magic-string: 0.30.11
|
||||
mlly: 1.7.1
|
||||
mlly: 1.7.2
|
||||
pathe: 1.1.2
|
||||
pkg-types: 1.2.0
|
||||
scule: 1.3.0
|
||||
@@ -7560,7 +7540,7 @@ snapshots:
|
||||
local-pkg: 0.5.0
|
||||
magic-string: 0.30.11
|
||||
minimatch: 9.0.5
|
||||
mlly: 1.7.1
|
||||
mlly: 1.7.2
|
||||
unplugin: 1.14.1(webpack-sources@3.2.3)
|
||||
vue: 3.5.10(typescript@5.6.2)
|
||||
optionalDependencies:
|
||||
@@ -7664,10 +7644,10 @@ snapshots:
|
||||
dependencies:
|
||||
vue: 3.5.10(typescript@5.6.2)
|
||||
|
||||
vue-eslint-parser@9.4.3(eslint@8.57.1):
|
||||
vue-eslint-parser@9.4.3(eslint@9.12.0):
|
||||
dependencies:
|
||||
debug: 4.3.7
|
||||
eslint: 8.57.1
|
||||
eslint: 9.12.0
|
||||
eslint-scope: 7.2.2
|
||||
eslint-visitor-keys: 3.4.3
|
||||
espree: 9.6.1
|
||||
@@ -7677,14 +7657,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vue-observe-visibility@2.0.0-alpha.1(vue@3.5.10(typescript@5.6.2)):
|
||||
dependencies:
|
||||
vue: 3.5.10(typescript@5.6.2)
|
||||
|
||||
vue-resize@2.0.0-alpha.1(vue@3.5.10(typescript@5.6.2)):
|
||||
dependencies:
|
||||
vue: 3.5.10(typescript@5.6.2)
|
||||
|
||||
vue-router@4.4.5(vue@3.5.10(typescript@5.6.2)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
@@ -7692,7 +7664,7 @@ snapshots:
|
||||
|
||||
vue-tsc@2.1.6(typescript@5.6.2):
|
||||
dependencies:
|
||||
'@volar/typescript': 2.4.5
|
||||
'@volar/typescript': 2.4.6
|
||||
'@vue/language-core': 2.1.6(typescript@5.6.2)
|
||||
semver: 7.6.3
|
||||
typescript: 5.6.2
|
||||
@@ -7702,13 +7674,6 @@ snapshots:
|
||||
vue: 3.5.10(typescript@5.6.2)
|
||||
vue-demi: 0.14.10(vue@3.5.10(typescript@5.6.2))
|
||||
|
||||
vue-virtual-scroller@2.0.0-beta.8(vue@3.5.10(typescript@5.6.2)):
|
||||
dependencies:
|
||||
mitt: 2.1.0
|
||||
vue: 3.5.10(typescript@5.6.2)
|
||||
vue-observe-visibility: 2.0.0-alpha.1(vue@3.5.10(typescript@5.6.2))
|
||||
vue-resize: 2.0.0-alpha.1(vue@3.5.10(typescript@5.6.2))
|
||||
|
||||
vue@3.5.10(typescript@5.6.2):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.10
|
||||
|
||||
@@ -57,3 +57,40 @@ export const uploadCloudSong = (file: File) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 云盘导入歌曲
|
||||
* @param {number} id - 歌曲 id
|
||||
* @param {string} song - 歌曲名称
|
||||
* @param {string} fileType - 歌曲格式
|
||||
* @param {number} fileSize - 歌曲大小
|
||||
* @param {number} bitrate - 歌曲比特率
|
||||
* @param {string} md5 - 歌曲 md5
|
||||
* @param {string} artist - 歌手
|
||||
* @param {string} album - 专辑
|
||||
*/
|
||||
export const importCloudSong = (
|
||||
song: string,
|
||||
fileType: string,
|
||||
fileSize: number,
|
||||
bitrate: number,
|
||||
md5: string,
|
||||
id?: number,
|
||||
artist?: string,
|
||||
album?: string,
|
||||
) => {
|
||||
return request({
|
||||
url: "/cloud/import",
|
||||
method: "POST",
|
||||
params: {id,
|
||||
song,
|
||||
fileType,
|
||||
fileSize,
|
||||
bitrate,
|
||||
md5,
|
||||
artist,
|
||||
album,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,160 +1,162 @@
|
||||
<template>
|
||||
<div :class="['song-card', { play: musicStore.playSong.id === song.id }]">
|
||||
<!-- 序号 -->
|
||||
<div class="num" @dblclick.stop>
|
||||
<n-text v-if="musicStore.playSong.id !== song.id" depth="3">
|
||||
{{ index + 1 }}
|
||||
</n-text>
|
||||
<SvgIcon v-else :size="22" name="Music" />
|
||||
<!-- 播放暂停 -->
|
||||
<SvgIcon
|
||||
:size="28"
|
||||
:name="statusStore.playStatus ? 'Pause' : 'Play'"
|
||||
class="status"
|
||||
@click="player.playOrPause()"
|
||||
/>
|
||||
<!-- 播放 -->
|
||||
<SvgIcon :size="28" name="Play" class="play" @click="player.addNextSong(song, true)" />
|
||||
</div>
|
||||
<!-- 标题 -->
|
||||
<div class="title">
|
||||
<!-- 封面 -->
|
||||
<s-image
|
||||
v-if="!hiddenCover"
|
||||
:key="song.cover"
|
||||
:src="song.path ? song.cover : song.coverSize?.s || song.cover"
|
||||
class="cover"
|
||||
@update:show.once="localCover"
|
||||
/>
|
||||
<!-- 信息 -->
|
||||
<div class="info">
|
||||
<!-- 名称 -->
|
||||
<div class="name">
|
||||
<n-ellipsis
|
||||
:line-clamp="1"
|
||||
:tooltip="{
|
||||
placement: 'top',
|
||||
width: 'trigger',
|
||||
}"
|
||||
class="name-text"
|
||||
>
|
||||
{{ song?.name || "未知曲目" }}
|
||||
</n-ellipsis>
|
||||
<!-- 音质 -->
|
||||
<n-tag
|
||||
v-if="song?.path && song?.quality"
|
||||
:bordered="false"
|
||||
:type="song.quality === 'Hi-Res' ? 'warning' : 'info'"
|
||||
class="quality"
|
||||
round
|
||||
>
|
||||
{{ song.quality }}
|
||||
</n-tag>
|
||||
<!-- 特权 -->
|
||||
<n-tag v-if="song.originCoverType === 1" :bordered="false" type="primary" round>
|
||||
原
|
||||
</n-tag>
|
||||
<n-tag v-if="song.free === 1" :bordered="false" type="error" round> VIP </n-tag>
|
||||
<n-tag v-if="song.free === 4" :bordered="false" type="error" round> EP </n-tag>
|
||||
<!-- 云盘 -->
|
||||
<n-tag v-if="song?.pc" :bordered="false" class="cloud" type="info" round>
|
||||
<template #icon>
|
||||
<SvgIcon name="Cloud" />
|
||||
</template>
|
||||
</n-tag>
|
||||
<!-- MV -->
|
||||
<n-tag
|
||||
v-if="song?.mv"
|
||||
:bordered="false"
|
||||
class="mv"
|
||||
type="warning"
|
||||
round
|
||||
@click.stop="
|
||||
router.push({
|
||||
name: 'video',
|
||||
query: { id: song.mv },
|
||||
})
|
||||
"
|
||||
>
|
||||
MV
|
||||
</n-tag>
|
||||
</div>
|
||||
<!-- 歌手 -->
|
||||
<div v-if="Array.isArray(song.artists)" class="artists text-hidden">
|
||||
<n-text
|
||||
v-for="ar in song.artists"
|
||||
:key="ar.id"
|
||||
class="ar"
|
||||
@click="openJumpArtist(song.artists)"
|
||||
>
|
||||
{{ ar.name }}
|
||||
</n-text>
|
||||
</div>
|
||||
<div v-else-if="song.type === 'radio'" class="artists">
|
||||
<n-text class="ar"> 电台节目 </n-text>
|
||||
</div>
|
||||
<div v-else class="artists text-hidden" @click="openJumpArtist(song.artists)">
|
||||
<n-text class="ar"> {{ song.artists || "未知艺术家" }} </n-text>
|
||||
</div>
|
||||
<!-- 别名 -->
|
||||
<n-text v-if="song.alia" class="alia" depth="3">{{ song.alia }}</n-text>
|
||||
<div class="song-card">
|
||||
<div :class="['song-content', { play: musicStore.playSong.id === song.id }]">
|
||||
<!-- 序号 -->
|
||||
<div class="num" @dblclick.stop>
|
||||
<n-text v-if="musicStore.playSong.id !== song.id" depth="3">
|
||||
{{ index + 1 }}
|
||||
</n-text>
|
||||
<SvgIcon v-else :size="22" name="Music" />
|
||||
<!-- 播放暂停 -->
|
||||
<SvgIcon
|
||||
:size="28"
|
||||
:name="statusStore.playStatus ? 'Pause' : 'Play'"
|
||||
class="status"
|
||||
@click="player.playOrPause()"
|
||||
/>
|
||||
<!-- 播放 -->
|
||||
<SvgIcon :size="28" name="Play" class="play" @click="player.addNextSong(song, true)" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 专辑 -->
|
||||
<div v-if="song.type !== 'radio' && !hiddenAlbum" class="album text-hidden">
|
||||
<n-text
|
||||
v-if="isObject(song.album)"
|
||||
class="album-text"
|
||||
@click="
|
||||
router.push({
|
||||
name: 'album',
|
||||
query: { id: song.album?.id },
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ song.album?.name || "未知专辑" }}
|
||||
<!-- 标题 -->
|
||||
<div class="title">
|
||||
<!-- 封面 -->
|
||||
<s-image
|
||||
v-if="!hiddenCover"
|
||||
:key="song.cover"
|
||||
:src="song.path ? song.cover : song.coverSize?.s || song.cover"
|
||||
class="cover"
|
||||
@update:show.once="localCover"
|
||||
/>
|
||||
<!-- 信息 -->
|
||||
<div class="info">
|
||||
<!-- 名称 -->
|
||||
<div class="name">
|
||||
<n-ellipsis
|
||||
:line-clamp="1"
|
||||
:tooltip="{
|
||||
placement: 'top',
|
||||
width: 'trigger',
|
||||
}"
|
||||
class="name-text"
|
||||
>
|
||||
{{ song?.name || "未知曲目" }}
|
||||
</n-ellipsis>
|
||||
<!-- 音质 -->
|
||||
<n-tag
|
||||
v-if="song?.path && song?.quality"
|
||||
:bordered="false"
|
||||
:type="song.quality === 'Hi-Res' ? 'warning' : 'info'"
|
||||
class="quality"
|
||||
round
|
||||
>
|
||||
{{ song.quality }}
|
||||
</n-tag>
|
||||
<!-- 特权 -->
|
||||
<n-tag v-if="song.originCoverType === 1" :bordered="false" type="primary" round>
|
||||
原
|
||||
</n-tag>
|
||||
<n-tag v-if="song.free === 1" :bordered="false" type="error" round> VIP </n-tag>
|
||||
<n-tag v-if="song.free === 4" :bordered="false" type="error" round> EP </n-tag>
|
||||
<!-- 云盘 -->
|
||||
<n-tag v-if="song?.pc" :bordered="false" class="cloud" type="info" round>
|
||||
<template #icon>
|
||||
<SvgIcon name="Cloud" />
|
||||
</template>
|
||||
</n-tag>
|
||||
<!-- MV -->
|
||||
<n-tag
|
||||
v-if="song?.mv"
|
||||
:bordered="false"
|
||||
class="mv"
|
||||
type="warning"
|
||||
round
|
||||
@click.stop="
|
||||
router.push({
|
||||
name: 'video',
|
||||
query: { id: song.mv },
|
||||
})
|
||||
"
|
||||
>
|
||||
MV
|
||||
</n-tag>
|
||||
</div>
|
||||
<!-- 歌手 -->
|
||||
<div v-if="Array.isArray(song.artists)" class="artists text-hidden">
|
||||
<n-text
|
||||
v-for="ar in song.artists"
|
||||
:key="ar.id"
|
||||
class="ar"
|
||||
@click="openJumpArtist(song.artists)"
|
||||
>
|
||||
{{ ar.name }}
|
||||
</n-text>
|
||||
</div>
|
||||
<div v-else-if="song.type === 'radio'" class="artists">
|
||||
<n-text class="ar"> 电台节目 </n-text>
|
||||
</div>
|
||||
<div v-else class="artists text-hidden" @click="openJumpArtist(song.artists)">
|
||||
<n-text class="ar"> {{ song.artists || "未知艺术家" }} </n-text>
|
||||
</div>
|
||||
<!-- 别名 -->
|
||||
<n-text v-if="song.alia" class="alia" depth="3">{{ song.alia }}</n-text>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 专辑 -->
|
||||
<div v-if="song.type !== 'radio' && !hiddenAlbum" class="album text-hidden">
|
||||
<n-text
|
||||
v-if="isObject(song.album)"
|
||||
class="album-text"
|
||||
@click="
|
||||
router.push({
|
||||
name: 'album',
|
||||
query: { id: song.album?.id },
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ song.album?.name || "未知专辑" }}
|
||||
</n-text>
|
||||
<n-text v-else class="album-text">
|
||||
{{ song.album || "未知专辑" }}
|
||||
</n-text>
|
||||
</div>
|
||||
<!-- 操作 -->
|
||||
<div v-if="song.type !== 'radio'" class="actions" @click.stop @dblclick.stop>
|
||||
<!-- 喜欢歌曲 -->
|
||||
<SvgIcon
|
||||
:name="dataStore.isLikeSong(song.id) ? 'Favorite' : 'FavoriteBorder'"
|
||||
:size="20"
|
||||
@click.stop="toLikeSong(song, !dataStore.isLikeSong(song.id))"
|
||||
@delclick.stop
|
||||
/>
|
||||
</div>
|
||||
<!-- 更新日期 -->
|
||||
<n-text v-if="song.type === 'radio'" class="meta date" depth="3">
|
||||
{{ formatTimestamp(song.updateTime) }}
|
||||
</n-text>
|
||||
<n-text v-else class="album-text">
|
||||
{{ song.album || "未知专辑" }}
|
||||
<!-- 播放量 -->
|
||||
<n-text v-if="song.type === 'radio'" class="meta" depth="3">
|
||||
{{ formatNumber(song.playCount || 0) }}
|
||||
</n-text>
|
||||
<!-- 时长 -->
|
||||
<n-text class="meta" depth="3">{{ msToTime(song.duration) }}</n-text>
|
||||
<!-- 大小 -->
|
||||
<n-text v-if="song.path && song.size && !hiddenSize" class="meta size" depth="3">
|
||||
{{ song.size }}M
|
||||
</n-text>
|
||||
</div>
|
||||
<!-- 操作 -->
|
||||
<div v-if="song.type !== 'radio'" class="actions" @click.stop @dblclick.stop>
|
||||
<!-- 喜欢歌曲 -->
|
||||
<SvgIcon
|
||||
:name="dataStore.isLikeSong(song.id) ? 'Favorite' : 'FavoriteBorder'"
|
||||
:size="20"
|
||||
@click.stop="toLikeSong(song, !dataStore.isLikeSong(song.id))"
|
||||
@delclick.stop
|
||||
/>
|
||||
</div>
|
||||
<!-- 更新日期 -->
|
||||
<n-text v-if="song.type === 'radio'" class="meta date" depth="3">
|
||||
{{ formatTimestamp(song.updateTime) }}
|
||||
</n-text>
|
||||
<!-- 播放量 -->
|
||||
<n-text v-if="song.type === 'radio'" class="meta" depth="3">
|
||||
{{ formatNumber(song.playCount || 0) }}
|
||||
</n-text>
|
||||
<!-- 时长 -->
|
||||
<n-text class="meta" depth="3">{{ msToTime(song.duration) }}</n-text>
|
||||
<!-- 大小 -->
|
||||
<n-text v-if="song.path && song.size && !hiddenSize" class="meta size" depth="3">
|
||||
{{ song.size }}M
|
||||
</n-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SongType } from "@/types/main";
|
||||
import { useStatusStore, useMusicStore, useDataStore } from "@/stores";
|
||||
import player from "@/utils/player";
|
||||
import { formatNumber, isElectron } from "@/utils/helper";
|
||||
import blob from "@/utils/blob";
|
||||
import { openJumpArtist } from "@/utils/modal";
|
||||
import { isObject } from "lodash-es";
|
||||
import { toLikeSong } from "@/utils/auth";
|
||||
import { isObject } from "lodash-es";
|
||||
import { formatTimestamp, msToTime } from "@/utils/time";
|
||||
import player from "@/utils/player";
|
||||
import blob from "@/utils/blob";
|
||||
|
||||
const props = defineProps<{
|
||||
// 歌曲
|
||||
@@ -172,35 +174,68 @@ const dataStore = useDataStore();
|
||||
const musicStore = useMusicStore();
|
||||
const statusStore = useStatusStore();
|
||||
|
||||
// 歌曲数据
|
||||
const song = toRef(props, "song");
|
||||
|
||||
// 加载本地歌曲封面
|
||||
const localCover = async (show: boolean) => {
|
||||
if (!isElectron || !show || !props.song.path) return;
|
||||
if (props.song.cover || props.song.cover === "/images/song.jpg?assest") return;
|
||||
if (!isElectron || !show || !song.value.path) return;
|
||||
if (song.value.cover || song.value.cover === "/images/song.jpg?assest") return;
|
||||
// 获取封面
|
||||
const coverData = await window.electron.ipcRenderer.invoke("get-music-cover", props.song.path);
|
||||
const coverData = await window.electron.ipcRenderer.invoke("get-music-cover", song.value.path);
|
||||
if (!coverData) return;
|
||||
const { data, format } = coverData;
|
||||
const blobURL = blob.createBlobURL(data, format, props.song.path);
|
||||
if (blobURL) props.song.cover = blobURL;
|
||||
const blobURL = blob.createBlobURL(data, format, song.value.path);
|
||||
if (blobURL) song.value.cover = blobURL;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.song-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
border-radius: 12px;
|
||||
padding: 8px 12px;
|
||||
border: 2px solid rgba(var(--primary), 0.12);
|
||||
background-color: var(--surface-container-hex);
|
||||
transition:
|
||||
background-color 0.3s var(--n-bezier),
|
||||
border-color 0.3s var(--n-bezier);
|
||||
cursor: pointer;
|
||||
.song-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
flex: 1;
|
||||
border-radius: 12px;
|
||||
border: 2px solid rgba(var(--primary), 0.12);
|
||||
background-color: var(--surface-container-hex);
|
||||
transition:
|
||||
background-color 0.3s var(--n-bezier),
|
||||
border-color 0.3s var(--n-bezier);
|
||||
&.play {
|
||||
border-color: rgba(var(--primary), 0.58);
|
||||
background-color: rgba(var(--primary), 0.28);
|
||||
}
|
||||
&:hover {
|
||||
border-color: rgba(var(--primary), 0.58);
|
||||
.num {
|
||||
.n-text,
|
||||
.n-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
.play {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
&.play {
|
||||
.num {
|
||||
.play {
|
||||
display: none;
|
||||
}
|
||||
.status {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.num {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -220,6 +255,9 @@ const localCover = async (show: boolean) => {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
transition:
|
||||
opacity 0.3s,
|
||||
transform 0.3s;
|
||||
&:active {
|
||||
opacity: 0.6 !important;
|
||||
}
|
||||
@@ -358,10 +396,6 @@ const localCover = async (show: boolean) => {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
&.play {
|
||||
border-color: rgba(var(--primary), 0.58);
|
||||
background-color: rgba(var(--primary), 0.28);
|
||||
}
|
||||
&.header {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!-- 全局图标 -->
|
||||
<template>
|
||||
<n-icon v-if="name" :size="size" :color="color" :depth="depth" v-html="svgContent" />
|
||||
<n-icon v-if="name" :size="size" :color="color" :depth="depth">
|
||||
<div ref="svgContainer" class="svg-container" />
|
||||
</n-icon>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -11,15 +13,17 @@ const props = defineProps<{
|
||||
depth?: 1 | 2 | 3 | 4 | 5;
|
||||
}>();
|
||||
|
||||
const svgContent = ref("");
|
||||
const svgContent = ref<string>("");
|
||||
const svgContainer = ref<HTMLElement | null>(null);
|
||||
|
||||
// 加载图标
|
||||
const loadSVG = async (name: string) => {
|
||||
try {
|
||||
const svg = await import(`../../assets/icons/${name}.svg?raw`);
|
||||
svgContent.value = svg.default || svg;
|
||||
if (svgContainer.value) svgContainer.value.innerHTML = svgContent.value;
|
||||
} catch (error) {
|
||||
console.error(`Could not load SVG for icon name: ${name}`);
|
||||
console.error(`Could not load SVG for icon name: ${name}`, error);
|
||||
svgContent.value = "";
|
||||
}
|
||||
};
|
||||
@@ -38,5 +42,11 @@ onMounted(() => loadSVG(props.name));
|
||||
padding: 0;
|
||||
// transition: all 0.3s;
|
||||
// color: var(--primary-hex);
|
||||
.svg-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary-hex);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,7 +52,7 @@ const updateScroll = () => {
|
||||
|
||||
// 滚动动画定时器
|
||||
let animationId: number | null = null;
|
||||
let scrollTimeoutId: NodeJS.Timeout | null = null;
|
||||
let scrollTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
// 开始滚动
|
||||
const startScrolling = () => {
|
||||
|
||||
@@ -1,312 +1,180 @@
|
||||
<!-- 歌曲列表 - 虚拟列表 -->
|
||||
<!-- vue-virt-list https://github.com/keno-lee/vue-virt-list -->
|
||||
<template>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div
|
||||
v-if="data.length > 0"
|
||||
v-if="!isEmpty(listData)"
|
||||
ref="songListRef"
|
||||
:style="{ height: disableVirtualList ? undefined : '100%' }"
|
||||
:class="['song-list', { 'no-padding': hiddenPadding }]"
|
||||
:class="[
|
||||
'song-list',
|
||||
{
|
||||
'hidden-scrollbar': hiddenScrollbar,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<DynamicScroller
|
||||
ref="scrollerRef"
|
||||
:items="listData"
|
||||
:min-item-size="94"
|
||||
:emitUpdate="true"
|
||||
:style="{ height: disableVirtualList ? undefined : `${songListShowHeight}px` }"
|
||||
class="scroller"
|
||||
@scroll="onScroll"
|
||||
>
|
||||
<template #before>
|
||||
<slot name="header" />
|
||||
<div class="song-item header" :style="{ margin }">
|
||||
<n-text class="num">#</n-text>
|
||||
<n-dropdown
|
||||
v-if="!disabledSort"
|
||||
:options="sortMenuOptions"
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
@select="sortSelect"
|
||||
>
|
||||
<div class="title has-sort">
|
||||
<n-text>标题</n-text>
|
||||
<n-text v-if="statusStore.listSort !== 'default'" class="sort" depth="3">
|
||||
{{ sortOptions[statusStore.listSort].name }}
|
||||
</n-text>
|
||||
</div>
|
||||
</n-dropdown>
|
||||
<n-text v-else class="title">标题</n-text>
|
||||
<n-text v-if="type !== 'radio' && !hiddenAlbum" class="album">专辑</n-text>
|
||||
<n-text v-if="type !== 'radio'" class="actions">操作</n-text>
|
||||
<n-text v-if="type === 'radio'" class="meta date">更新日期</n-text>
|
||||
<n-text v-if="type === 'radio'" class="meta">播放量</n-text>
|
||||
<n-text class="meta">时长</n-text>
|
||||
<n-text v-if="data?.[0].size && !hiddenSize" class="meta size">大小</n-text>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot="{ item, index, active }">
|
||||
<DynamicScrollerItem
|
||||
:item="item"
|
||||
:active="active"
|
||||
:data-index="index"
|
||||
:key="item.id"
|
||||
class="song-item-wrapper"
|
||||
>
|
||||
<div
|
||||
:class="['song-item', { play: musicStore.playSong.id === item.id }]"
|
||||
:style="{ margin }"
|
||||
@dblclick.stop="playSong(item)"
|
||||
@contextmenu="
|
||||
songListMenuRef?.openDropdown($event, data, item, index, type, playListId)
|
||||
"
|
||||
>
|
||||
<!-- 序号 -->
|
||||
<div class="num" @dblclick.stop>
|
||||
<n-text v-if="musicStore.playSong.id !== item.id" depth="3">
|
||||
{{ index + 1 }}
|
||||
</n-text>
|
||||
<SvgIcon v-else :size="22" name="Music" />
|
||||
<!-- 播放暂停 -->
|
||||
<SvgIcon
|
||||
:size="28"
|
||||
:name="statusStore.playStatus ? 'Pause' : 'Play'"
|
||||
class="status"
|
||||
@click="player.playOrPause()"
|
||||
/>
|
||||
<!-- 播放 -->
|
||||
<SvgIcon
|
||||
:size="28"
|
||||
name="Play"
|
||||
class="play"
|
||||
@click="player.addNextSong(item, true)"
|
||||
/>
|
||||
</div>
|
||||
<!-- 标题 -->
|
||||
<div class="title">
|
||||
<!-- 封面 -->
|
||||
<s-image
|
||||
v-if="!hiddenCover"
|
||||
:key="item.cover"
|
||||
:src="item.path ? item.cover : item.coverSize?.s || item.cover"
|
||||
class="cover"
|
||||
@update:show.once="(show: boolean) => localCover(show, item?.path, index)"
|
||||
/>
|
||||
<!-- 信息 -->
|
||||
<div class="info">
|
||||
<!-- 名称 -->
|
||||
<div class="name">
|
||||
<n-ellipsis
|
||||
:line-clamp="1"
|
||||
:tooltip="{
|
||||
placement: 'top',
|
||||
width: 'trigger',
|
||||
}"
|
||||
class="name-text"
|
||||
>
|
||||
{{ item?.name || "未知曲目" }}
|
||||
</n-ellipsis>
|
||||
<!-- 音质 -->
|
||||
<n-tag
|
||||
v-if="item?.path && item?.quality"
|
||||
:bordered="false"
|
||||
:type="item.quality === 'Hi-Res' ? 'warning' : 'info'"
|
||||
class="quality"
|
||||
round
|
||||
>
|
||||
{{ item.quality }}
|
||||
</n-tag>
|
||||
<!-- 特权 -->
|
||||
<n-tag v-if="item.originCoverType === 1" :bordered="false" type="primary" round>
|
||||
原
|
||||
</n-tag>
|
||||
<n-tag v-if="item.free === 1" :bordered="false" type="error" round> VIP </n-tag>
|
||||
<n-tag v-if="item.free === 4" :bordered="false" type="error" round> EP </n-tag>
|
||||
<!-- 云盘 -->
|
||||
<n-tag v-if="item?.pc" :bordered="false" class="cloud" type="info" round>
|
||||
<template #icon>
|
||||
<SvgIcon name="Cloud" />
|
||||
</template>
|
||||
</n-tag>
|
||||
<!-- MV -->
|
||||
<n-tag
|
||||
v-if="item?.mv"
|
||||
:bordered="false"
|
||||
class="mv"
|
||||
type="warning"
|
||||
round
|
||||
@click.stop="
|
||||
router.push({
|
||||
name: 'video',
|
||||
query: { id: item.mv },
|
||||
})
|
||||
"
|
||||
>
|
||||
MV
|
||||
</n-tag>
|
||||
</div>
|
||||
<!-- 歌手 -->
|
||||
<div v-if="Array.isArray(item.artists)" class="artists text-hidden">
|
||||
<n-text
|
||||
v-for="ar in item.artists"
|
||||
:key="ar.id"
|
||||
class="ar"
|
||||
@click="openJumpArtist(item.artists)"
|
||||
>
|
||||
{{ ar.name }}
|
||||
</n-text>
|
||||
</div>
|
||||
<div v-else-if="type === 'radio'" class="artists">
|
||||
<n-text class="ar"> 电台节目 </n-text>
|
||||
</div>
|
||||
<div v-else class="artists text-hidden" @click="openJumpArtist(item.artists)">
|
||||
<n-text class="ar"> {{ item.artists || "未知艺术家" }} </n-text>
|
||||
</div>
|
||||
<!-- 别名 -->
|
||||
<n-text v-if="item.alia" class="alia" depth="3">{{ item.alia }}</n-text>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<VirtList
|
||||
ref="listRef"
|
||||
:key="listData?.[0]?.id"
|
||||
:list="listData"
|
||||
:minSize="94"
|
||||
:buffer="2"
|
||||
:offset="offset"
|
||||
:style="{ height: height === 'auto' ? 'auto' : `${height || songListHeight}px` }"
|
||||
itemKey="id"
|
||||
@scroll="onScroll"
|
||||
@toBottom="onToBottom"
|
||||
>
|
||||
<!-- 悬浮顶栏 -->
|
||||
<template #stickyHeader>
|
||||
<div class="list-header song-card">
|
||||
<n-text class="num">#</n-text>
|
||||
<n-dropdown
|
||||
v-if="!disabledSort"
|
||||
:options="sortMenuOptions"
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
@select="sortSelect"
|
||||
>
|
||||
<div class="title has-sort">
|
||||
<n-text>标题</n-text>
|
||||
<n-text v-if="statusStore.listSort !== 'default'" class="sort" depth="3">
|
||||
{{ sortOptions[statusStore.listSort].name }}
|
||||
</n-text>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 专辑 -->
|
||||
<div v-if="type !== 'radio' && !hiddenAlbum" class="album text-hidden">
|
||||
<n-text
|
||||
v-if="isObject(item.album)"
|
||||
class="album-text"
|
||||
@click="
|
||||
router.push({
|
||||
name: 'album',
|
||||
query: { id: item.album?.id },
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ item.album?.name || "未知专辑" }}
|
||||
</n-text>
|
||||
<n-text v-else class="album-text">
|
||||
{{ item.album || "未知专辑" }}
|
||||
</n-text>
|
||||
</div>
|
||||
<!-- 操作 -->
|
||||
<div v-if="type !== 'radio'" class="actions" @click.stop @dblclick.stop>
|
||||
<!-- 喜欢歌曲 -->
|
||||
<SvgIcon
|
||||
:name="dataStore.isLikeSong(item.id) ? 'Favorite' : 'FavoriteBorder'"
|
||||
:size="20"
|
||||
@click.stop="toLikeSong(item, !dataStore.isLikeSong(item.id))"
|
||||
@delclick.stop
|
||||
/>
|
||||
</div>
|
||||
<!-- 更新日期 -->
|
||||
<n-text v-if="type === 'radio'" class="meta date" depth="3">
|
||||
{{ formatTimestamp(item.updateTime) }}
|
||||
</n-text>
|
||||
<!-- 播放量 -->
|
||||
<n-text v-if="type === 'radio'" class="meta" depth="3">
|
||||
{{ formatNumber(item.playCount) }}
|
||||
</n-text>
|
||||
<!-- 时长 -->
|
||||
<n-text class="meta" depth="3">{{ msToTime(item.duration) }}</n-text>
|
||||
<!-- 大小 -->
|
||||
<n-text v-if="item.path && item.size && !hiddenSize" class="meta size" depth="3">
|
||||
{{ item.size }}M
|
||||
</n-text>
|
||||
</n-dropdown>
|
||||
<n-text v-else class="title">标题</n-text>
|
||||
<n-text v-if="type !== 'radio' && !hiddenAlbum" class="album">专辑</n-text>
|
||||
<n-text v-if="type !== 'radio'" class="actions">操作</n-text>
|
||||
<n-text v-if="type === 'radio'" class="meta date">更新日期</n-text>
|
||||
<n-text v-if="type === 'radio'" class="meta">播放量</n-text>
|
||||
<n-text class="meta">时长</n-text>
|
||||
<n-text v-if="data?.[0].size && !hiddenSize" class="meta size">大小</n-text>
|
||||
</div>
|
||||
</DynamicScrollerItem>
|
||||
</template>
|
||||
<template #after>
|
||||
<div class="list-after">
|
||||
<n-flex v-if="loadMore && loading">
|
||||
<n-spin size="small" />
|
||||
<n-text>{{ loadingText || "努力加载中" }}</n-text>
|
||||
</n-flex>
|
||||
<n-divider v-else dashed> 没有更多啦 ~ </n-divider>
|
||||
</div>
|
||||
</template>
|
||||
</DynamicScroller>
|
||||
</template>
|
||||
<!-- 主内容 -->
|
||||
<template #default="{ itemData, index }">
|
||||
<SongCard
|
||||
:song="itemData"
|
||||
:index="index"
|
||||
:hiddenCover="hiddenCover"
|
||||
:hiddenAlbum="hiddenAlbum"
|
||||
:hiddenSize="hiddenSize"
|
||||
@dblclick.stop="player.updatePlayList(listData, itemData, playListId)"
|
||||
@contextmenu.stop="
|
||||
songListMenuRef?.openDropdown($event, listData, itemData, index, type, playListId)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<!-- 加载更多 -->
|
||||
<template #footer>
|
||||
<div class="load-more">
|
||||
<n-flex v-if="loadMore && loading">
|
||||
<n-spin size="small" />
|
||||
<n-text>{{ loadingText || "努力加载中" }}</n-text>
|
||||
</n-flex>
|
||||
<n-divider v-else dashed> 没有更多啦 ~ </n-divider>
|
||||
</div>
|
||||
</template>
|
||||
</VirtList>
|
||||
</Transition>
|
||||
<!-- 右键菜单 -->
|
||||
<SongListMenu ref="songListMenuRef" @removeSong="removeSong" />
|
||||
<!-- 列表操作 -->
|
||||
<Teleport to="body">
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-float-button-group v-if="floatToolShow && !disableVirtualList" class="list-button">
|
||||
<n-float-button-group v-if="floatToolShow" class="list-menu">
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-float-button v-if="songListScrollTop > 100" width="42" @click="scrollTo(0)">
|
||||
<n-float-button v-if="scrollTop > 100" width="42" @click="listRef?.scrollToTop()">
|
||||
<SvgIcon :size="22" name="Up" />
|
||||
</n-float-button>
|
||||
</Transition>
|
||||
<n-float-button v-if="hasPlaySong >= 0" width="42" @click="scrollTo(hasPlaySong)">
|
||||
<n-float-button
|
||||
v-if="hasPlaySong >= 0"
|
||||
width="42"
|
||||
@click="listRef?.scrollToIndex(hasPlaySong)"
|
||||
>
|
||||
<SvgIcon :size="22" name="Location" />
|
||||
</n-float-button>
|
||||
</n-float-button-group>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</div>
|
||||
<!-- 加载动画 -->
|
||||
<!-- 列表加载 - 骨架屏 -->
|
||||
<div v-else-if="loading" class="song-list loading">
|
||||
<n-skeleton :repeat="10" text />
|
||||
</div>
|
||||
<!-- 空列表 -->
|
||||
<n-empty v-else description="空空如也,怎么一首歌都没有啊" size="large" class="song-list" />
|
||||
<n-empty v-else description="列表光秃秃的,啥都没有哦" size="large" class="song-list empty" />
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SongType, SortType } from "@/types/main";
|
||||
import type { DropdownOption } from "naive-ui";
|
||||
import { useStatusStore, useMusicStore, useDataStore } from "@/stores";
|
||||
import { isObject, entries, cloneDeep } from "lodash-es";
|
||||
import { openJumpArtist } from "@/utils/modal";
|
||||
import { formatNumber, isElectron } from "@/utils/helper";
|
||||
import type { SongType, SortType } from "@/types/main";
|
||||
import { useMusicStore, useStatusStore } from "@/stores";
|
||||
import { VirtList } from "vue-virt-list";
|
||||
import { cloneDeep, entries, isEmpty } from "lodash-es";
|
||||
import { sortOptions } from "@/utils/meta";
|
||||
import { toLikeSong } from "@/utils/auth";
|
||||
import { formatTimestamp, msToTime } from "@/utils/time";
|
||||
import SongListMenu from "@/components/Menu/SongListMenu.vue";
|
||||
import player from "@/utils/player";
|
||||
import blob from "@/utils/blob";
|
||||
|
||||
const router = useRouter();
|
||||
const dataStore = useDataStore();
|
||||
const musicStore = useMusicStore();
|
||||
const statusStore = useStatusStore();
|
||||
|
||||
interface Props {
|
||||
data: SongType[];
|
||||
type?: "song" | "radio";
|
||||
loadMore?: boolean;
|
||||
loading?: boolean;
|
||||
loadingText?: string;
|
||||
hiddenAlbum?: boolean;
|
||||
hiddenCover?: boolean;
|
||||
hiddenPadding?: boolean;
|
||||
hiddenSize?: boolean;
|
||||
margin?: string;
|
||||
height?: number;
|
||||
// 禁用排序
|
||||
disabledSort?: boolean;
|
||||
// 播放歌单 ID
|
||||
playListId?: number;
|
||||
// 禁用虚拟列表
|
||||
disableVirtualList?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: "song",
|
||||
});
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 列表数据
|
||||
data: SongType[];
|
||||
// 列表类型
|
||||
type?: "song" | "radio";
|
||||
// 列表高度
|
||||
height?: number | "auto"; // px
|
||||
// 是否加载
|
||||
loading?: boolean;
|
||||
// 加载更多
|
||||
loadMore?: boolean;
|
||||
loadingText?: string;
|
||||
// 隐藏元素
|
||||
hiddenAlbum?: boolean;
|
||||
hiddenCover?: boolean;
|
||||
hiddenSize?: boolean;
|
||||
// 隐藏滚动条
|
||||
hiddenScrollbar?: boolean;
|
||||
// 禁用排序
|
||||
disabledSort?: boolean;
|
||||
// 播放歌单 ID
|
||||
playListId?: number;
|
||||
}>(),
|
||||
{
|
||||
type: "song",
|
||||
loadingText: "努力加载中...",
|
||||
playListId: 0,
|
||||
},
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
// 触底
|
||||
reachBottom: [];
|
||||
reachBottom: [e: Event];
|
||||
// 滚动
|
||||
scroll: [e: Event];
|
||||
// 删除歌曲
|
||||
removeSong: [id: number[]];
|
||||
}>();
|
||||
|
||||
// 右键菜单
|
||||
const songListMenuRef = ref<InstanceType<typeof SongListMenu> | null>(null);
|
||||
const musicStore = useMusicStore();
|
||||
const statusStore = useStatusStore();
|
||||
|
||||
// 列表状态
|
||||
const offset = ref<number>(0);
|
||||
const scrollTop = ref<number>(0);
|
||||
|
||||
// 列表元素
|
||||
const scrollerRef = ref<any | null>(null);
|
||||
const listRef = ref<InstanceType<typeof VirtList> | null>(null);
|
||||
const songListRef = ref<HTMLElement | null>(null);
|
||||
const songListScrollTop = ref<number>(0);
|
||||
|
||||
// 悬浮工具
|
||||
const floatToolShow = ref<boolean>(false);
|
||||
const floatToolShow = ref<boolean>(true);
|
||||
|
||||
// 右键菜单
|
||||
const songListMenuRef = ref<InstanceType<typeof SongListMenu> | null>(null);
|
||||
|
||||
// 列表数据
|
||||
const listData = computed<SongType[]>(() => {
|
||||
@@ -343,17 +211,14 @@ const listData = computed<SongType[]>(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// 列表元素高度
|
||||
const { height: songListHeight, stop: stopHeight } = useElementSize(songListRef);
|
||||
|
||||
// 应该展示的列表高度
|
||||
const songListShowHeight = computed(() => props.height ?? songListHeight.value);
|
||||
|
||||
// 列表是否具有播放歌曲
|
||||
const hasPlaySong = computed(() => {
|
||||
return listData.value.findIndex((item) => item.id === musicStore.playSong.id);
|
||||
});
|
||||
|
||||
// 列表元素高度
|
||||
const { height: songListHeight, stop: stopCalcHeight } = useElementSize(songListRef);
|
||||
|
||||
// 列表排序菜单
|
||||
const sortMenuOptions = computed<DropdownOption[]>(() =>
|
||||
entries(sortOptions).map(([key, { name, show, icon }]) => ({
|
||||
@@ -364,6 +229,18 @@ const sortMenuOptions = computed<DropdownOption[]>(() =>
|
||||
})),
|
||||
);
|
||||
|
||||
// 列表滚动
|
||||
const onScroll = (e: Event) => {
|
||||
emit("scroll", e);
|
||||
scrollTop.value = (e.target as HTMLElement).scrollTop;
|
||||
};
|
||||
|
||||
// 列表触底
|
||||
const onToBottom = (e: Event) => {
|
||||
if (props.loading) return;
|
||||
emit("reachBottom", e);
|
||||
};
|
||||
|
||||
// 排序更改
|
||||
const sortSelect = (key: SortType) => {
|
||||
statusStore.listSort = key;
|
||||
@@ -375,102 +252,53 @@ const sortSelect = (key: SortType) => {
|
||||
scrobble: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 滚动至播放歌曲
|
||||
const scrollTo = (index: number) => {
|
||||
if (index === 0) songListScrollTop.value = 0;
|
||||
scrollerRef.value?.scrollToItem(index);
|
||||
};
|
||||
|
||||
// 加载本地歌曲封面
|
||||
const localCover = async (show: boolean, path: string, index: number) => {
|
||||
if (!isElectron || !show || !path) return;
|
||||
if (listData.value[index].cover || listData.value[index].cover === "/images/song.jpg?assest")
|
||||
return;
|
||||
// 获取封面
|
||||
const coverData = await window.electron.ipcRenderer.invoke("get-music-cover", path);
|
||||
if (!coverData) return;
|
||||
const { data, format } = coverData;
|
||||
const blobURL = blob.createBlobURL(data, format, path);
|
||||
if (blobURL) listData.value[index].cover = blobURL;
|
||||
};
|
||||
|
||||
// 播放列表歌曲
|
||||
const playSong = (song: SongType) => {
|
||||
console.log(song);
|
||||
// 更改播放列表
|
||||
player.updatePlayList(listData.value, song, props.playListId);
|
||||
};
|
||||
|
||||
// 列表滚动
|
||||
const onScroll = (e: Event) => {
|
||||
const target = e.target as HTMLElement | null;
|
||||
// 获取高度
|
||||
if (target && target.scrollTop) {
|
||||
songListScrollTop.value = target.scrollTop;
|
||||
}
|
||||
// 是否触底
|
||||
const offset: number = 300;
|
||||
if (target && target.scrollTop + target.clientHeight >= target.scrollHeight - offset) {
|
||||
if (props.loadMore && !props.loading) emit("reachBottom");
|
||||
}
|
||||
// 滚动事件
|
||||
emit("scroll", e);
|
||||
// 滚动到顶部
|
||||
listRef.value?.scrollToIndex(hasPlaySong.value || 0);
|
||||
};
|
||||
|
||||
// 删除指定索引
|
||||
const removeSong = (id: number[]) => emit("removeSong", id);
|
||||
|
||||
onDeactivated(() => {
|
||||
// keep-alive 处理
|
||||
onBeforeRouteLeave(() => {
|
||||
offset.value = listRef.value?.getOffset() || 0;
|
||||
floatToolShow.value = false;
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
floatToolShow.value = true;
|
||||
if (props.disableVirtualList) stopHeight();
|
||||
if (props.height === "auto") stopCalcHeight();
|
||||
if (offset.value > 0) listRef.value?.scrollToOffset(offset.value);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopCalcHeight();
|
||||
floatToolShow.value = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.song-list {
|
||||
.scroller {
|
||||
padding-bottom: 14px;
|
||||
transition: height 0.3s;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
&::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(var(--primary), 0.28);
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
.song-item-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 94px;
|
||||
height: 100%;
|
||||
.song-card {
|
||||
padding-bottom: 12px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.song-item {
|
||||
// 悬浮顶栏
|
||||
.list-header {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
// min-height: 70px;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
border-radius: 12px;
|
||||
padding: 8px 12px;
|
||||
border: 2px solid rgba(var(--primary), 0.12);
|
||||
background-color: var(--surface-container-hex);
|
||||
transition:
|
||||
background-color 0.3s var(--n-bezier),
|
||||
border-color 0.3s var(--n-bezier);
|
||||
cursor: pointer;
|
||||
padding: 8px 18px 8px 12px;
|
||||
margin-right: 4px;
|
||||
border: 1px solid transparent;
|
||||
background-color: var(--background-hex);
|
||||
.n-text {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.num {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -480,106 +308,14 @@ onActivated(() => {
|
||||
min-width: 40px;
|
||||
font-weight: bold;
|
||||
margin-right: 12px;
|
||||
.n-icon {
|
||||
transition:
|
||||
opacity 0.3s,
|
||||
transform 0.3s;
|
||||
}
|
||||
.status,
|
||||
.play {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
&:active {
|
||||
opacity: 0.6 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.title {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 20px 4px 0;
|
||||
.cover {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
min-width: 50px;
|
||||
border-radius: 8px;
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.name {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
:deep(.name-text) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.n-tag {
|
||||
--n-height: 20px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
margin-right: 6px;
|
||||
pointer-events: none;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.quality {
|
||||
font-size: 10px;
|
||||
}
|
||||
.cloud {
|
||||
padding: 0 10px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
:deep(.n-tag__icon) {
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.n-icon {
|
||||
font-size: 12px;
|
||||
color: var(--n-text-color);
|
||||
}
|
||||
}
|
||||
.mv {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
.artists {
|
||||
margin-top: 2px;
|
||||
font-size: 13px;
|
||||
.ar {
|
||||
display: inline-flex;
|
||||
transition: opacity 0.3s;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
&::after {
|
||||
content: "/";
|
||||
margin: 0 4px;
|
||||
}
|
||||
&:last-child {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
.alia {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
cursor: pointer;
|
||||
.sort {
|
||||
margin-left: 6px;
|
||||
&::after {
|
||||
@@ -589,33 +325,35 @@ onActivated(() => {
|
||||
content: "( ";
|
||||
}
|
||||
}
|
||||
&.has-sort {
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
top: 0;
|
||||
left: -8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(var(--primary), 0.08);
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
&:hover {
|
||||
&::after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.album {
|
||||
flex: 1;
|
||||
line-clamp: 2;
|
||||
-webkit-line-clamp: 2;
|
||||
padding-right: 20px;
|
||||
&:hover {
|
||||
.album-text {
|
||||
color: var(--primary-hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
.n-icon {
|
||||
transition: transform 0.3s;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
&:active {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
.meta {
|
||||
width: 50px;
|
||||
@@ -628,74 +366,45 @@ onActivated(() => {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
&.play {
|
||||
border-color: rgba(var(--primary), 0.58);
|
||||
background-color: rgba(var(--primary), 0.28);
|
||||
}
|
||||
&.header {
|
||||
border: none;
|
||||
}
|
||||
// 滚动条
|
||||
.virt-list__client {
|
||||
transition:
|
||||
height 0.3s,
|
||||
width 0.3s,
|
||||
opacity 0.3s;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
background-color: transparent;
|
||||
.n-text {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.title {
|
||||
position: relative;
|
||||
padding: 0 20px 0 0;
|
||||
&.has-sort {
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
top: 0;
|
||||
left: -8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(var(--primary), 0.08);
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
&:hover {
|
||||
&::after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(var(--primary), 0.28);
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
&.hidden-scrollbar {
|
||||
.list-header {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
.song-card {
|
||||
padding-right: 0;
|
||||
}
|
||||
.virt-list__client {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.vue-recycle-scroller__item-view) {
|
||||
&.hover {
|
||||
.song-item {
|
||||
border-color: rgba(var(--primary), 0.58);
|
||||
.num {
|
||||
.n-text,
|
||||
.n-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
.play {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
&.play {
|
||||
.num {
|
||||
.play {
|
||||
display: none;
|
||||
}
|
||||
.status {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-after {
|
||||
// 加载更多
|
||||
.load-more {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
margin: 20px 0 40px;
|
||||
.n-spin-body {
|
||||
--n-size: 20px;
|
||||
}
|
||||
@@ -705,14 +414,21 @@ onActivated(() => {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
&.no-padding {
|
||||
margin: 0 -24px;
|
||||
.song-item {
|
||||
margin: 0px 21px 0px 26px;
|
||||
// 加载
|
||||
&.loading {
|
||||
margin-top: 20px;
|
||||
:deep(.n-skeleton) {
|
||||
height: 72px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
// 空列表
|
||||
&.empty {
|
||||
margin-top: 60px;
|
||||
}
|
||||
}
|
||||
.list-button {
|
||||
.list-menu {
|
||||
position: fixed;
|
||||
right: 40px;
|
||||
bottom: 120px;
|
||||
@@ -721,15 +437,4 @@ onActivated(() => {
|
||||
border: 1px solid rgba(var(--primary), 0.28);
|
||||
}
|
||||
}
|
||||
.loading {
|
||||
margin-top: 20px !important;
|
||||
:deep(.n-skeleton) {
|
||||
height: 72px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
.n-empty {
|
||||
margin-top: 60px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,15 +20,16 @@ import type { SongType } from "@/types/main";
|
||||
import { NAlert, type DropdownOption } from "naive-ui";
|
||||
import { useStatusStore, useLocalStore, useDataStore } from "@/stores";
|
||||
import { renderIcon, copyData } from "@/utils/helper";
|
||||
import { deleteCloudSong } from "@/api/cloud";
|
||||
import { deleteCloudSong, importCloudSong } from "@/api/cloud";
|
||||
import {
|
||||
openCloudMatch,
|
||||
openDownloadSong,
|
||||
openPlaylistAdd,
|
||||
openSongInfoEditor,
|
||||
} from "@/utils/modal";
|
||||
import player from "@/utils/player";
|
||||
import { deleteSongs } from "@/utils/auth";
|
||||
import { songUrl } from "@/api/song";
|
||||
import player from "@/utils/player";
|
||||
|
||||
const emit = defineEmits<{ removeSong: [index: number[]] }>();
|
||||
|
||||
@@ -165,6 +166,15 @@ const openDropdown = (
|
||||
key: "line-two",
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
key: "cloud-import",
|
||||
label: "导入至云盘",
|
||||
show: !isCloud && type === "song" && !isLocal,
|
||||
props: {
|
||||
onClick: () => importSongToCloud(song),
|
||||
},
|
||||
icon: renderIcon("Cloud"),
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
label: "从歌单中删除",
|
||||
@@ -290,6 +300,27 @@ const deleteCloudSongData = (song: SongType, index: number) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 导入至云盘
|
||||
const importSongToCloud = async (song: SongType) => {
|
||||
if (!song?.id) return;
|
||||
// 获取歌曲下载信息
|
||||
const songData = await songUrl(song.id);
|
||||
const songDetail = songData?.data?.[0];
|
||||
// 开始尝试导入
|
||||
const { id, type, size, br, md5 } = songDetail;
|
||||
const result = await importCloudSong(song?.name, type, size, Math.floor(br / 1000), md5, id);
|
||||
if (result.code === 200) {
|
||||
const failed = result?.data?.failed?.[0];
|
||||
if (failed?.code !== -200) {
|
||||
window.$message.success("导入成功");
|
||||
} else {
|
||||
window.$message.error(failed?.msg || "导入失败,请重试");
|
||||
}
|
||||
} else {
|
||||
window.$message.error("导入失败,请重试");
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ openDropdown });
|
||||
</script>
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ const checkQrStatus = async () => {
|
||||
window.$message.error("登录出错,请重试");
|
||||
getQrData();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -598,9 +598,6 @@ const changeVolume = (e: WheelEvent) => {
|
||||
width: 64px;
|
||||
height: 200px;
|
||||
padding: 12px 16px;
|
||||
.n-slider {
|
||||
--n-rail-width-vertical: 18px;
|
||||
}
|
||||
.slider-num {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
>
|
||||
<SvgIcon name="AddList" />
|
||||
</div>
|
||||
<!-- 下载 -->
|
||||
<div class="menu-icon" @click.stop="openDownloadSong(musicStore.playSong)">
|
||||
<SvgIcon name="Download" />
|
||||
</div>
|
||||
</n-flex>
|
||||
<div class="center">
|
||||
<div class="btn">
|
||||
@@ -109,7 +113,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useMusicStore, useStatusStore, useDataStore } from "@/stores";
|
||||
import { secondsToTime, calculateCurrentTime } from "@/utils/time";
|
||||
import { openPlaylistAdd } from "@/utils/modal";
|
||||
import { openDownloadSong, openPlaylistAdd } from "@/utils/modal";
|
||||
import { toLikeSong } from "@/utils/auth";
|
||||
import player from "@/utils/player";
|
||||
|
||||
|
||||
@@ -73,6 +73,13 @@
|
||||
</div>
|
||||
<n-switch v-model:value="settingStore.useSongUnlock" class="set" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="label">
|
||||
<n-text class="name">听歌打卡</n-text>
|
||||
<n-text class="tip" :depth="3">是否将播放歌曲同步至网易云音乐</n-text>
|
||||
</div>
|
||||
<n-switch v-model:value="settingStore.scrobbleSong" class="set" :round="false" />
|
||||
</n-card>
|
||||
<n-card v-if="isElectron" class="set-item">
|
||||
<div class="label">
|
||||
<n-text class="name">音频输出设备</n-text>
|
||||
|
||||
3
src/env.d.ts
vendored
3
src/env.d.ts
vendored
@@ -2,7 +2,6 @@
|
||||
|
||||
declare module "*.vue" {
|
||||
import type { DefineComponent } from "vue";
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
const component: DefineComponent<object, object, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@ import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||
import router from "@/router";
|
||||
// 自定义指令
|
||||
import { debounceDirective, throttleDirective, visibleDirective } from "@/utils/instruction";
|
||||
// VueVirtualScroller
|
||||
import VueVirtualScroller from "vue-virtual-scroller";
|
||||
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
|
||||
// ipc
|
||||
import initIpc from "@/utils/initIpc";
|
||||
// 全局样式
|
||||
@@ -28,8 +25,6 @@ pinia.use(piniaPluginPersistedstate);
|
||||
app.use(pinia);
|
||||
// router
|
||||
app.use(router);
|
||||
// VueVirtualScroller
|
||||
app.use(VueVirtualScroller);
|
||||
// 自定义指令
|
||||
app.directive("debounce", debounceDirective);
|
||||
app.directive("throttle", throttleDirective);
|
||||
|
||||
@@ -141,6 +141,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "/radio",
|
||||
name: "radio",
|
||||
|
||||
beforeEnter: (to, _, next) => {
|
||||
if (!to.query.id) next({ path: "/403" });
|
||||
else next();
|
||||
|
||||
@@ -81,6 +81,7 @@ interface SettingState {
|
||||
useRealIP: boolean;
|
||||
realIP: string;
|
||||
fullPlayerCache: boolean;
|
||||
scrobbleSong: boolean;
|
||||
}
|
||||
|
||||
export const useSettingStore = defineStore({
|
||||
@@ -123,6 +124,7 @@ export const useSettingStore = defineStore({
|
||||
smtcOpen: true, // 是否开启 SMTC
|
||||
smtcOutputHighQualityCover: false, // 是否输出高清封面
|
||||
playSongDemo: false, // 是否播放试听歌曲
|
||||
scrobbleSong: false, // 是否打卡
|
||||
// 歌词
|
||||
lyricFontSize: 46, // 歌词大小
|
||||
lyricTranFontSize: 22, // 歌词翻译大小
|
||||
@@ -161,11 +163,13 @@ export const useSettingStore = defineStore({
|
||||
setThemeMode(mode?: "auto" | "light" | "dark") {
|
||||
// 若未传入
|
||||
if (mode === undefined) {
|
||||
this.themeMode === "auto"
|
||||
? (this.themeMode = "light")
|
||||
: this.themeMode === "light"
|
||||
? (this.themeMode = "dark")
|
||||
: (this.themeMode = "auto");
|
||||
if (this.themeMode === "auto") {
|
||||
this.themeMode = "light";
|
||||
} else if (this.themeMode === "light") {
|
||||
this.themeMode = "dark";
|
||||
} else {
|
||||
this.themeMode = "auto";
|
||||
}
|
||||
} else {
|
||||
this.themeMode = mode;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@ import { formatCoverList, formatArtistsList, formatSongsList } from "@/utils/for
|
||||
import { useDataStore, useMusicStore } from "@/stores";
|
||||
import { logout, refreshLogin } from "@/api/login";
|
||||
import { openUserLogin } from "./modal";
|
||||
import { debounce } from "lodash-es";
|
||||
import { debounce, isFunction } from "lodash-es";
|
||||
import { isBeforeSixAM } from "./time";
|
||||
import { dailyRecommend } from "@/api/rec";
|
||||
import { isElectron } from "./helper";
|
||||
import { playlistTracks } from "@/api/playlist";
|
||||
import { likePlaylist, playlistTracks } from "@/api/playlist";
|
||||
import { likeArtist } from "@/api/artist";
|
||||
import { radioSub } from "@/api/radio";
|
||||
|
||||
// 是否登录
|
||||
export const isLogin = () => !!getCookie("MUSIC_U");
|
||||
@@ -195,6 +197,75 @@ export const toLikeSong = debounce(
|
||||
{ leading: true, trailing: false },
|
||||
);
|
||||
|
||||
// 收藏/取消收藏歌单
|
||||
export const toLikePlaylist = debounce(
|
||||
async (id: number, like: boolean) => {
|
||||
if (!id) return;
|
||||
if (!isLogin()) {
|
||||
window.$message.warning("请登录后使用");
|
||||
openUserLogin();
|
||||
return;
|
||||
}
|
||||
const { code } = await likePlaylist(id, like ? 1 : 2);
|
||||
if (code === 200) {
|
||||
window.$message.success((like ? "收藏" : "取消收藏") + "歌单成功");
|
||||
// 更新
|
||||
await updateUserLikePlaylist();
|
||||
} else {
|
||||
window.$message.success((like ? "收藏" : "取消收藏") + "歌单失败,请重试");
|
||||
return;
|
||||
}
|
||||
},
|
||||
300,
|
||||
{ leading: true, trailing: false },
|
||||
);
|
||||
|
||||
// 收藏/取消收藏歌手
|
||||
export const toLikeArtist = debounce(
|
||||
async (id: number, like: boolean) => {
|
||||
if (!id) return;
|
||||
if (!isLogin()) {
|
||||
window.$message.warning("请登录后使用");
|
||||
openUserLogin();
|
||||
return;
|
||||
}
|
||||
const { code } = await likeArtist(id, like ? 1 : 2);
|
||||
if (code === 200) {
|
||||
window.$message.success((like ? "收藏" : "取消收藏") + "歌手成功");
|
||||
// 更新
|
||||
await updateUserLikeArtists();
|
||||
} else {
|
||||
window.$message.success((like ? "收藏" : "取消收藏") + "歌手失败,请重试");
|
||||
return;
|
||||
}
|
||||
},
|
||||
300,
|
||||
{ leading: true, trailing: false },
|
||||
);
|
||||
|
||||
// 订阅/取消订阅播客
|
||||
export const toSubRadio = debounce(
|
||||
async (id: number, like: boolean) => {
|
||||
if (!id) return;
|
||||
if (!isLogin()) {
|
||||
window.$message.warning("请登录后使用");
|
||||
openUserLogin();
|
||||
return;
|
||||
}
|
||||
const { code } = await radioSub(id, like ? 1 : 0);
|
||||
if (code === 200) {
|
||||
window.$message.success((like ? "订阅" : "取消订阅") + "播客成功");
|
||||
// 更新
|
||||
await updateUserLikeDjs();
|
||||
} else {
|
||||
window.$message.success((like ? "订阅" : "取消订阅") + "播客失败,请重试");
|
||||
return;
|
||||
}
|
||||
},
|
||||
300,
|
||||
{ leading: true, trailing: false },
|
||||
);
|
||||
|
||||
// 循环获取用户喜欢数据
|
||||
const setUserLikeDataLoop = async <T>(
|
||||
apiFunction: (limit: number, offset: number) => Promise<{ data: any[]; count: number }>,
|
||||
@@ -279,7 +350,7 @@ export const deleteSongs = async (pid: number, ids: number[], callback?: () => v
|
||||
window.$message.error(result.body?.message || "删除歌曲失败,请重试");
|
||||
return;
|
||||
}
|
||||
callback && callback();
|
||||
if (isFunction(callback)) callback();
|
||||
window.$message.success("删除成功");
|
||||
} else {
|
||||
window.$message.error(result?.message || "删除歌曲失败,请重试");
|
||||
|
||||
@@ -3,7 +3,9 @@ import { useEventListener } from "@vueuse/core";
|
||||
import { openUserAgreement } from "@/utils/modal";
|
||||
import { debounce } from "lodash-es";
|
||||
import { isElectron } from "./helper";
|
||||
import packageJson from "@/../package.json";
|
||||
import player from "@/utils/player";
|
||||
import log from "./log";
|
||||
|
||||
// 应用初始化时需要执行的操作
|
||||
const init = async () => {
|
||||
@@ -13,6 +15,8 @@ const init = async () => {
|
||||
const settingStore = useSettingStore();
|
||||
const shortcutStore = useShortcutStore();
|
||||
|
||||
printVersion();
|
||||
|
||||
// 用户协议
|
||||
openUserAgreement();
|
||||
|
||||
@@ -101,4 +105,11 @@ const keyDownEvent = debounce((event: KeyboardEvent) => {
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// 版本输出
|
||||
const printVersion = async () => {
|
||||
log.success(`🚀 ${packageJson.version}`, packageJson.productName);
|
||||
log.info(`👤 ${packageJson.author}`, packageJson.github);
|
||||
|
||||
};
|
||||
|
||||
export default init;
|
||||
|
||||
97
src/utils/log.ts
Normal file
97
src/utils/log.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 美化打印实现方法
|
||||
* https://juejin.cn/post/7371716384847364147
|
||||
*/
|
||||
const log = () => {
|
||||
const isEmpty = (value: any) => {
|
||||
return value == null || value === undefined || value === "";
|
||||
};
|
||||
const prettyPrint = (title: string, text: string, color: string) => {
|
||||
console.info(
|
||||
`%c ${title} %c ${text} %c`,
|
||||
`background:${color};border:1px solid ${color}; padding: 1px; border-radius: 2px 0 0 2px; color: #fff;`,
|
||||
`border:1px solid ${color}; padding: 1px; border-radius: 0 2px 2px 0; color: ${color};`,
|
||||
"background:transparent",
|
||||
);
|
||||
};
|
||||
const info = (textOrTitle: string, content = "") => {
|
||||
const title = isEmpty(content) ? "Info" : textOrTitle;
|
||||
const text = isEmpty(content) ? textOrTitle : content;
|
||||
prettyPrint(title, text, "#909399");
|
||||
};
|
||||
const error = (textOrTitle: string, content = "") => {
|
||||
const title = isEmpty(content) ? "Error" : textOrTitle;
|
||||
const text = isEmpty(content) ? textOrTitle : content;
|
||||
prettyPrint(title, text, "#F56C6C");
|
||||
};
|
||||
const warning = (textOrTitle: string, content = "") => {
|
||||
const title = isEmpty(content) ? "Warning" : textOrTitle;
|
||||
const text = isEmpty(content) ? textOrTitle : content;
|
||||
prettyPrint(title, text, "#E6A23C");
|
||||
};
|
||||
const success = (textOrTitle: string, content = "") => {
|
||||
const title = isEmpty(content) ? "Success " : textOrTitle;
|
||||
const text = isEmpty(content) ? textOrTitle : content;
|
||||
prettyPrint(title, text, "#67C23A");
|
||||
};
|
||||
const table = () => {
|
||||
const data = [
|
||||
{ id: 1, name: "Alice", age: 25 },
|
||||
{ id: 2, name: "Bob", age: 30 },
|
||||
{ id: 3, name: "Charlie", age: 35 },
|
||||
];
|
||||
console.info(
|
||||
"%c id%c name%c age",
|
||||
"color: white; background-color: black; padding: 2px 10px;",
|
||||
"color: white; background-color: black; padding: 2px 10px;",
|
||||
"color: white; background-color: black; padding: 2px 10px;",
|
||||
);
|
||||
|
||||
data.forEach((row: any) => {
|
||||
console.info(
|
||||
`%c ${row.id} %c ${row.name} %c ${row.age} `,
|
||||
"color: black; background-color: lightgray; padding: 2px 10px;",
|
||||
"color: black; background-color: lightgray; padding: 2px 10px;",
|
||||
"color: black; background-color: lightgray; padding: 2px 10px;",
|
||||
);
|
||||
});
|
||||
};
|
||||
const picture = (url: string, scale = 1) => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = "anonymous";
|
||||
img.onload = () => {
|
||||
const c = document.createElement("canvas");
|
||||
const ctx = c.getContext("2d");
|
||||
if (ctx) {
|
||||
c.width = img.width;
|
||||
c.height = img.height;
|
||||
ctx.fillStyle = "red";
|
||||
ctx.fillRect(0, 0, c.width, c.height);
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const dataUri = c.toDataURL("image/png");
|
||||
|
||||
console.info(
|
||||
`%c sup?`,
|
||||
`font-size: 1px;
|
||||
padding: ${Math.floor((img.height * scale) / 2)}px ${Math.floor((img.width * scale) / 2)}px;background-image: url(${dataUri});
|
||||
background-repeat: no-repeat;
|
||||
background-size: ${img.width * scale}px ${img.height * scale}px;
|
||||
color: transparent;`,
|
||||
);
|
||||
}
|
||||
};
|
||||
img.src = url;
|
||||
};
|
||||
|
||||
// retu;
|
||||
return {
|
||||
info,
|
||||
error,
|
||||
warning,
|
||||
success,
|
||||
picture,
|
||||
table,
|
||||
};
|
||||
};
|
||||
|
||||
export default log();
|
||||
@@ -176,7 +176,7 @@ export const openUpdatePlaylist = (id: number, data: CoverType, func: () => Prom
|
||||
onSuccess: () => {
|
||||
modal.destroy();
|
||||
// 触发回调
|
||||
isFunction(func) && func();
|
||||
if (isFunction(func)) func();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -616,7 +616,8 @@ class Player {
|
||||
*/
|
||||
playOrPause() {
|
||||
const statusStore = useStatusStore();
|
||||
statusStore.playStatus ? this.pause() : this.play();
|
||||
if (statusStore.playStatus) this.pause();
|
||||
else this.play();
|
||||
}
|
||||
/**
|
||||
* 下一首或上一首
|
||||
@@ -835,7 +836,7 @@ class Player {
|
||||
// 获取配置
|
||||
const { showTip, scrobble, play } = options;
|
||||
// 打卡
|
||||
scrobble && this.scrobbleSong();
|
||||
if (scrobble) this.scrobbleSong();
|
||||
// 更新列表
|
||||
await dataStore.setPlayList(cloneDeep(data));
|
||||
// 关闭特殊模式
|
||||
@@ -1088,8 +1089,10 @@ class Player {
|
||||
async scrobbleSong() {
|
||||
const musicStore = useMusicStore();
|
||||
const statusStore = useStatusStore();
|
||||
const settingStore = useSettingStore();
|
||||
try {
|
||||
if (!isLogin()) return;
|
||||
if (!settingStore.scrobbleSong) return;
|
||||
// 获取所需数据
|
||||
const playSongData = this.getPlaySongData();
|
||||
if (!playSongData) return;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="data">
|
||||
<div class="name">
|
||||
<div class="name text-hidden">
|
||||
<n-text class="name-text">{{ artistDetailData.name || "未知艺术家" }}</n-text>
|
||||
<n-text v-if="artistDetailData?.alia" class="name-alias" depth="3">
|
||||
{{ artistDetailData.alia || "未知艺术家" }}
|
||||
@@ -89,7 +89,13 @@
|
||||
</template>
|
||||
播放
|
||||
</n-button>
|
||||
<n-button :focusable="false" strong secondary round>
|
||||
<n-button
|
||||
:focusable="false"
|
||||
strong
|
||||
secondary
|
||||
round
|
||||
@click="toLikeArtist(artistId, !isLikeArtist)"
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon :name="isLikeArtist ? 'Favorite' : 'FavoriteBorder'" />
|
||||
</template>
|
||||
@@ -145,6 +151,7 @@ import { renderToolbar } from "@/utils/meta";
|
||||
import { artistDetail } from "@/api/artist";
|
||||
import { formatArtistsList } from "@/utils/format";
|
||||
import { useDataStore, useSettingStore } from "@/stores";
|
||||
import { toLikeArtist } from "@/utils/auth";
|
||||
import ArtistSongs from "./songs.vue";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</n-flex>
|
||||
<!-- 列表 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<SongList v-if="!searchValue || searchData?.length" :data="listData" :loading="loading" />
|
||||
<SongList v-if="!searchValue || searchData?.length" :data="listDataShow" :loading="loading" />
|
||||
<n-empty
|
||||
v-else
|
||||
:description="`搜不到关于 ${searchValue} 的任何歌曲呀`"
|
||||
@@ -116,7 +116,7 @@ const searchValue = ref<string>("");
|
||||
const searchData = ref<SongType[]>([]);
|
||||
|
||||
// 列表歌曲
|
||||
const listData = computed<SongType[]>(() => {
|
||||
const listDataShow = computed<SongType[]>(() => {
|
||||
if (searchValue.value && searchData.value.length) return searchData.value;
|
||||
return cloudData.value;
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</n-flex>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<SongList :data="musicStore.dailySongsData.list" :loading="true" disableVirtualList />
|
||||
<SongList :data="musicStore.dailySongsData.list" :loading="true" height="auto" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -108,5 +108,8 @@ onMounted(updateDailySongsData);
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
.song-list {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -139,7 +139,6 @@ onMounted(getAllNewData);
|
||||
margin-top: 20px;
|
||||
}
|
||||
.song-list {
|
||||
margin-top: 20px;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<template #info>
|
||||
<div
|
||||
v-for="(song, songIndex) in item.tracks"
|
||||
:key="index"
|
||||
:key="songIndex"
|
||||
class="song-item text-hidden"
|
||||
>
|
||||
<n-text class="name">{{ songIndex + 1 }}. {{ song.first }}</n-text>
|
||||
|
||||
@@ -39,18 +39,9 @@
|
||||
v-if="dataStore.historyList.length > 0"
|
||||
:data="dataStore.historyList"
|
||||
:loading="true"
|
||||
hiddenPadding
|
||||
hiddenCover
|
||||
hiddenSize
|
||||
/>
|
||||
<!-- <SongListNew
|
||||
v-if="dataStore.historyList.length > 0"
|
||||
:data="dataStore.historyList"
|
||||
:loading="true"
|
||||
hiddenPadding
|
||||
hiddenCover
|
||||
hiddenSize
|
||||
/> -->
|
||||
<n-empty
|
||||
v-else
|
||||
description="暂无记录,快去播放一些歌曲吧"
|
||||
@@ -111,7 +102,7 @@ const cleanHistory = () => {
|
||||
}
|
||||
.menu {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 12px;
|
||||
.n-button {
|
||||
height: 40px;
|
||||
transition: all 0.3s var(--n-bezier);
|
||||
|
||||
@@ -141,7 +141,6 @@
|
||||
:data="albumDataShow"
|
||||
:loading="loading"
|
||||
:height="songListHeight"
|
||||
hidden-padding
|
||||
hidden-album
|
||||
@scroll="listScroll"
|
||||
/>
|
||||
|
||||
@@ -149,7 +149,6 @@
|
||||
:loading="loading"
|
||||
:height="songListHeight"
|
||||
:playListId="playlistId"
|
||||
hidden-padding
|
||||
@scroll="listScroll"
|
||||
@removeSong="removeSong"
|
||||
/>
|
||||
|
||||
@@ -138,7 +138,14 @@
|
||||
</template>
|
||||
编辑歌单
|
||||
</n-button>
|
||||
<n-button v-else :focusable="false" strong secondary round>
|
||||
<n-button
|
||||
v-else
|
||||
:focusable="false"
|
||||
strong
|
||||
secondary
|
||||
round
|
||||
@click="toLikePlaylist(playlistId, !isLikePlaylist)"
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon :name="isLikePlaylist ? 'Favorite' : 'FavoriteBorder'" />
|
||||
</template>
|
||||
@@ -187,7 +194,6 @@
|
||||
:loading="loading"
|
||||
:height="songListHeight"
|
||||
:playListId="playlistId"
|
||||
hidden-padding
|
||||
@scroll="listScroll"
|
||||
@removeSong="removeSong"
|
||||
/>
|
||||
@@ -213,7 +219,7 @@ import { playlistDetail, playlistAllSongs, deletePlaylist } from "@/api/playlist
|
||||
import { formatCoverList, formatSongsList } from "@/utils/format";
|
||||
import { coverLoaded, formatNumber, fuzzySearch, renderIcon } from "@/utils/helper";
|
||||
import { renderToolbar } from "@/utils/meta";
|
||||
import { isLogin, updateUserLikePlaylist } from "@/utils/auth";
|
||||
import { isLogin, toLikePlaylist, updateUserLikePlaylist } from "@/utils/auth";
|
||||
import { debounce } from "lodash-es";
|
||||
import { useDataStore, useStatusStore } from "@/stores";
|
||||
import { openBatchList, openUpdatePlaylist } from "@/utils/modal";
|
||||
|
||||
@@ -101,11 +101,17 @@
|
||||
: "播放"
|
||||
}}
|
||||
</n-button>
|
||||
<n-button :focusable="false" strong secondary round>
|
||||
<n-button
|
||||
:focusable="false"
|
||||
strong
|
||||
secondary
|
||||
round
|
||||
@click="toSubRadio(radioId, !isLikeRadio)"
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon :name="isLikePlaylist ? 'Favorite' : 'FavoriteBorder'" />
|
||||
<SvgIcon :name="isLikeRadio ? 'Favorite' : 'FavoriteBorder'" />
|
||||
</template>
|
||||
{{ isLikePlaylist ? "取消收藏" : "收藏播客" }}
|
||||
{{ isLikeRadio ? "取消订阅" : "订阅播客" }}
|
||||
</n-button>
|
||||
<!-- 更多 -->
|
||||
<n-dropdown :options="moreOptions" trigger="click" placement="bottom-start">
|
||||
@@ -151,7 +157,6 @@
|
||||
:height="songListHeight"
|
||||
:radioId="radioId"
|
||||
type="radio"
|
||||
hidden-padding
|
||||
@scroll="listScroll"
|
||||
/>
|
||||
<n-empty
|
||||
@@ -179,6 +184,7 @@ import { useDataStore, useStatusStore } from "@/stores";
|
||||
import { radioAllProgram, radioDetail } from "@/api/radio";
|
||||
import player from "@/utils/player";
|
||||
import { formatTimestamp } from "@/utils/time";
|
||||
import { toSubRadio } from "@/utils/auth";
|
||||
|
||||
const router = useRouter();
|
||||
const dataStore = useDataStore();
|
||||
@@ -214,14 +220,12 @@ const songListHeight = computed(() => {
|
||||
});
|
||||
|
||||
// 是否处于收藏播客
|
||||
const isLikePlaylist = computed(() => {
|
||||
return dataStore.userLikeData.playlists.some(
|
||||
(playlist) => playlist.id === radioDetailData.value?.id,
|
||||
);
|
||||
const isLikeRadio = computed(() => {
|
||||
return dataStore.userLikeData.djs.some((radio) => radio.id === radioDetailData.value?.id);
|
||||
});
|
||||
|
||||
// 是否处于播客页面
|
||||
const isPlaylistPage = computed<boolean>(() => router.currentRoute.value.name === "playlist");
|
||||
const isPlaylistPage = computed<boolean>(() => router.currentRoute.value.name === "radio");
|
||||
|
||||
// 是否为相同播客
|
||||
const isSamePlaylist = computed<boolean>(() => oldRadioId.value === radioId.value);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
:key="chooseArtist"
|
||||
:data="chooseArtist ? artistData[chooseArtist] : []"
|
||||
:loading="true"
|
||||
hidden-cover
|
||||
:hidden-cover="!settingStore.showLocalCover"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user